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

4

4 回答 4

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是否. 如果是,则将副本插入容器并避免克隆。baseDerivedClass

于 2012-09-26T12:35:24.137 回答
3

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

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

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

[更新]

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

还有一个警告:它可能会base->clone()返回比派生更多的东西:

class MoreDerivedClass : public DerivedClass {};

即使真正的基类是,此代码也会成功MoreDerivedClass

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

你可以typeid()用来检查基地的真实类型......


[更新 2]
考虑改变你的设计 - 并将你的基地克隆保留在 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()));
    }
}
于 2012-09-26T12:37:21.267 回答
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));
}

this 的语义与您的语义略有不同,因为您的代码将允许*objectis 的派生类型比严格更严格的情况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);
    }
}
于 2012-09-26T12:58:28.807 回答
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. 上面的代码没有这样做。

于 2012-09-26T12:51:14.390 回答