我想在一个抽象类的std :: vector中存储从公共接口(抽象类)派生的类的对象。这个向量应该填充一个循环,通常我会调用一个类的构造函数并将创建的对象推送到向量中。
据我所知,在抽象类的情况下,我只能存储指向该类的指针,所以我需要push_back派生类的指针。但是,我不确定这些新创建的对象的范围。
请看下面的代码。此代码编译并正常工作,但我的问题是:
a)对象是否保证存在于main函数的第二个for-loop中?或者他们是否可以在创建它们的循环范围之外停止存在?
b)是否所有物品都是'调用析构函数或可能存在内存泄漏?
#include<vector>
#include<iostream>
class Interface {
public:
Interface( int y ) : x(y) {}
virtual ~Interface() {}
virtual void f() = 0;
int x;
};
class Derived_A : public Interface {
public:
Derived_A( int y ) : Interface(y) {}
void f(){ return; }
};
class Derived_B : public Interface {
public:
Derived_B( int y ) : Interface(y) {}
void f(){ return; }
};
int main()
{
std::vector<Interface*> abstractObjects;
int N = 5;
for(int ii = 0; ii < N; ii++ )
{
abstractObjects.push_back( new Derived_A(ii) );
abstractObjects.push_back( new Derived_B(ii) );
}
for(int ii = 0; ii < abstractObjects.size(); ii++ )
{
abstractObjects[ii]->f();
std::cout << abstractObjects[ii]->x << '\t' << std::endl;
}
for(int ii = 0; ii < abstractObjects.size(); ii++ )
{
delete abstractObjects[ii];
}
return 0;
}
答案 0 :(得分:9)
这是智能指针的完美案例。您可以将指针存储在RAI类型的unique_ptr
中。当unique_ptr
超出范围时,它会自动为您删除内存。
//...
std::vector<std::unique_ptr<Interface>> abstractObjects;
int N = 5;
for(int ii = 0; ii < N; ii++ )
{
abstractObjects.push_back( std::make_unique<Derived_A>(ii) );
abstractObjects.push_back( std::make_unique<Derived_B>(ii) );
}
for(auto & e : abstractObjects) // ranged based for loop
{
e->f();
std::cout << e->x << '\t' << std::endl;
}
// no need to do anything here. the vector will get rid of each unique_ptr and each unique_ptr will delete each pointer
return 0;
}
答案 1 :(得分:4)
让我谈谈你的观点。
a)对象是否保证存在于第二个for循环中 主功能?或者他们可能会停止超出范围的存在 它们被创建的循环?
当您调用关键字new
时,对象将一直存在,直到您明确调用delete
来释放相关内存。如果您在堆栈上创建了对象,则在第一个循环终止后它们将超出范围。
b)是否所有对象的析构函数都被调用或存在内存泄漏?
是的,您正在调用最终循环中每个对象的析构函数,并且通常不会出现内存泄漏。但是,如果在到达最终循环之前抛出异常,则不会回收分配的内存,并且将发生泄漏。请参阅this post。
但是,您可以通过使用智能指针来改进代码,智能指针可以通过自动回收内存来解决问题。使用std::make_unique<Derived_A>(ii)
而不是new Derived_A(ii)
,当向量超出范围时,它将自动为其包含的每个对象释放相关内存,从而无需在最终循环中自己显式调用析构函数。
答案 2 :(得分:2)
是的,对象仍存在于您的第一个for
循环范围之外,因此您对delete
他们是正确的。
不会自动调用所有对象的析构函数。如果您new
某事,那么您必须delete
。但是你可以(而且应该)使用智能指针。
std::vector<std::unique_ptr<Interface>> abstractObjects;
int N = 5;
for(int ii = 0; ii < N; ii++ )
{
abstractObjects.push_back( std::make_unique<Derived_A>(ii) );
abstractObjects.push_back( std::make_unique<Derived_B>(ii) );
}
现在你没有delete
任何东西,当vector
超出范围时会调用析构函数(所以它的所有元素都是如此)
答案 3 :(得分:-1)
我建议使用{{1>} 智能指针(而不是原始拥有指针)。虽然原始观察指针很好,但拥有一个原始拥有指针的向量可能是潜在的泄漏源。
对于非共享所有权,请考虑使用 vector
,并使用vector<unique_ptr<Interface>>
动态创建要添加到向量中的新项目。
使用 smart 指针的向量,可以编写更简单,更清晰的代码,因为C ++析构函数可以自动完成清理(换句话说,所有手动清理)使用原始拥有指针向量所需的代码就会消失。)