删除动态创建的对象

时间:2012-09-26 12:31:38

标签: c++ memory c++11

在我的应用程序中,我正在创建并返回一个数组,该数组填充了派生类中动态分配的对象,如下所示:

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而不是对象吗?

4 个答案:

答案 0 :(得分: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。如果是,则将副本插入容器中,避免克隆。

答案 1 :(得分:3)

简单回答 - 总是删除基础。

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

Vector获取派生的副本 - 因此不再需要克隆对象。

[UPDATE]

我希望您在BaseClass中拥有虚拟析构函数 - 如果没有 - 然后添加它。

还有一个警告: base->clone()可能会发生更多派生而非派生的内容:

class MoreDerivedClass : public DerivedClass {};

然后,即使真正的基类为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()));
    }
}

答案 2 :(得分: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));
}

这与你的语义略有不同,因为你的代码允许*objectDerivedClass更严格地派生类型的情况,并且复制到向量中切片对象。目前的代码没有遇到这个问题。


更新(在您的评论之后):如果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);
    }
}

答案 3 :(得分: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的整个点。上面的代码没有这样做。