2012-09-26 117 views
0

在我的应用我创建并返回充满了动态分配的对象从派生类像这样的数组:删除动态创建的对象

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    BaseClass* base = object->clone(); // "object" is a "unique_ptr<BaseClass>" 

    DerivedClass* derived = dynamic_cast<DerivedClass*> (base); 

    if (derived != nullptr) 
    { 
     returnV.push_back(*derived); 
    } 
    else 
    { 
     delete base; 
    } 
} 

这显然造成内存泄漏(valgrinds帮助我在这里),因为派生永远不会被删除。

我试图释放所分配的内存是这样的:

delete &returnV[0]; 

它不给任何编译错误/警告和代码仍然运行正常。但valgrind会在该行代码中报告一些额外的错误(无效读取,无效的自由),并且泄漏仍然存在。

有没有什么方法可以释放像这样返回的内存?或者我应该返回unique_ptr而不是对象?

+0

东西(以及作为地下室里的人)尖叫“可怕的虐待”......如果你有'EvenMoreDerivedClass'呢?这将在副本中瓜分... –

+0

对于C++ 11的问题:你真的应该使用'的std :: unique_ptr'(或'的std :: shared_ptr',根据您的使用情况)。手动内存管理实际上不被推荐,因为内存泄漏的潜力(由于缺少像代码中的delete一样,或者由于不处理引发异常的情况)非常高。所以我的建议永远不会(通常有一些例外,尽管不是很多)使用手动管理,并始终使用智能指针(或诸如boost指针容器之类的东西)。 – Grizzly

+0

@Grizzly与Kerrek的第二个答案我不这样做手工的内存管理了 – Minion91

回答

2

首先,设计似乎很可疑的对我说:你有一次一个多态的层次结构,并持有值该层次结构中的特定成员的的容器。你邀请的问题没有尽头。有一个std::vector<std::unique_ptr<Base>>似乎更合理。

总之,这里的适度安全,高效插入容器中唯一的那些对象的动态类型精确匹配方式。它假定层次结构中的每个类都有可访问的拷贝构造函数。

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    if (typeid(*object) != typeid(DerivedClass)) { return; } 

    returnV.insert(static_cast<DerivedClass&>(*object)); 
} 

的这个语义是从你的略有不同,因为您的代码将允许其中*object是严格的多个派生类型比DerivedClass的情况下,和复制到载体将切片的对象。目前的代码不会遇到这个问题。


更新(您的评论后):如果DerivedClass确实final(并请注明它是这样!),那么下面确实没有typeid:我内心

void someGetter(std:vector<DerivedClass> & returnV) 
{ 
    if (DerivedClass * p = dynamic_cast<DerivedClass *>(object.get())) 
    { 
     assert(typeid(*p) == typeid(DerivedClass)); // beware of slicing! 

     returnV.insert(*p); 
    } 
} 
+0

嗯,起初我使用的是unique_ptr,但是我的项目协调员让我把它变成值。 Wat例外是你想赶上?为什么一个static_cast? – Minion91

+0

你可以假设DerivedClass永远不会被子类化。 – Minion91

+0

@ Minion91:对不起,如果你让我使用复制构造函数,异常问题就会消失。如果你坚持使用'clone'(如果你还想要价值语义,这很愚蠢),那么你必须处理异常。 –

6

如果你要创建一个保存Derived那么代码是除了内存泄漏正确的向量。请注意,您需要释放的对象不是一个容器中(这是一个副本),而是你克隆了一个:

void someGetter(std:vector<DerivedClass>& returnV) 
{ 
    BaseClass* base = object->clone(); (object is a unique_ptr<BaseClass>) 
    DerivedClass* derived = dynamic_cast<DerivedClass> (base); 
    if (derived != nullptr) 
    { 
     returnV.push_back(*derived); 
    } 
    delete base; 
} 

此外,如果clone()做什么它说(即它克隆的对象)那么您可以先用dynamic_cast进行测试,以避免该操作base对象是否为 a DerivedClass。如果它插入到容器中,则避免克隆。

+0

克隆不正是它说,这样的push_back自动外币对象的副本?与拷贝构造函数? – Minion91

+0

它甚至会更快做' typeid的(* objec t)== typeid(DerivedClass)',非?也更安全,因为它不会切分和切割“EvenMoreDerivedClass”。 –

+0

@ Minion91:是的,容器容纳*值*,它将使用复制构造函数存储对象的一个​​副本(在您的情况下,在其他一些情况下,它可能*在C++ 11中移动*或构建到位。 。但无论如何容器将持有自己的副本传入的对象) –

3

简单的答案 - 总是删除基地。

if (derived != nullptr) 
{ 
    returnV.push_back(*derived); 
} 
delete base; 

向量需要派生的副本 - 因此不再需要克隆对象。

[更新]

我希望你有虚析构函数中BaseClass - 如果不是 - 然后将其添加。

还有一个警告:可能会发生:base->clone()回报的东西比得到的多源性:

那么这个代码成功,即使真正的阶级基础是MoreDerivedClass

DerivedClass* derived = dynamic_cast<DerivedClass> (base); 

您可以使用typeid()来检查实际类型的基地...


[UPDATE2]
考虑改变一点你的设计 - 并保持你的基地的克隆在DerivedClass中的unique_ptr的载体:

void someGetter(std:vector<std::unique_ptr<DerivedClass>> & returnV) 
{ 
    if (dynamic_cast<DerivedClass*>(base.get()) != nullptr) 
    { 
     returnV.push_back(dynamic_cast<DerivedClass*>(base->clone())); 
    } 
} 
1

是,push_back使用拷贝构造函数。我认为大卫是说,你的代码应该是

void someGetter(std:vector<DerivedClass>& returnV) 
{ 
    DerivedClass*derived = dynamic_cast<DerivedClass*>(object.get()); 
    if (derived != nullptr) 
     returnV.push_back(*derived); 
} 

避免克隆和删除完全。

注加入编辑:我们不得通过从unique_ptr<>.get()获得的指针上可能潜在地保留一份,不畏unique_ptr整点的任何功能。上面的代码没有这样做。

+0

是不是用得到那种邪恶?它的魅力击败了unique_ptr的目的不是? – Minion91

+0

@ Minion91 **否** 1)如果它本身就是邪恶的,那么它就不在标准库中。 2)在这里我们没有泄漏指针(你可以用'.get()'来完成),而只是将它用作指针,即我们将它传递给一个需要实际指针类型的函数。 – Walter

+0

那么你在哪里干恶劣之间的界限呢?因为你当然可以用unique_ptr.get() – Minion91