C++ 删除动态创建的对象

C++ 删除动态创建的对象,c++,memory,c++11,C++,Memory,C++11,在我的应用程序中,我创建并返回一个数组,数组中填充了派生类中的动态分配对象,如下所示: void someGetter(std:vector<DerivedClass> & returnV) { BaseClass* base = object->clone(); // "object" is a "unique_ptr<BaseClass>" DerivedClass* derived = dynamic_cast<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);
    }
    else
    {
        delete base;
    }
}
delete &returnV[0];
它不会给出任何编译错误/警告,代码仍然可以正常运行。但是valgrind报告了该行代码中的一些额外错误(无效读取、无效释放),泄漏仍然存在


有没有办法释放像这样返回的内存?或者我应该返回unique_ptr而不是objects吗?

如果您要创建一个包含
派生
类型值的向量,那么除了内存泄漏之外,代码是正确的。请注意,需要释放的对象不是容器中的对象(副本),而是克隆的对象:

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;
}
void someGetter(标准:vector&returnV)
{
BaseClass*base=object->clone();(对象是唯一的_ptr)
DerivedClass*derived=动态施法(基本);
if(派生的!=nullptr)
{
返回v.推回(*导出);
}
删除基数;
}

此外,如果
clone()
按照它所说的做(即克隆对象),则可以通过首先使用
dynamic\u cast
测试
base
对象是否为
DerivedClass
来避免该操作。如果是,则将副本插入容器中并避免克隆。

简单回答-始终删除base

if (derived != nullptr)
{
    returnV.push_back(*derived);
}
delete base;
Vector获取派生对象的副本,因此不再需要克隆对象

[更新]

我希望您在
基类中有虚拟析构函数
-如果没有,请添加它。

还有一个警告:可能会发生
base->clone()
返回比派生更派生的内容:

class MoreDerivedClass : public DerivedClass {};
即使基类的实际类是
MoreDerivedClass
,该代码也会成功:

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

是,
push_back
使用复制构造函数。我想David是说你的代码应该是

void someGetter(std:vector<DerivedClass>&  returnV)
{
    DerivedClass*derived = dynamic_cast<DerivedClass*>(object.get());
    if (derived != nullptr)
        returnV.push_back(*derived);
}
void someGetter(标准:vector&returnV)
{
DerivedClass*derived=dynamic_cast(object.get());
if(派生的!=nullptr)
返回v.推回(*导出);
}
完全避免克隆和删除


在编辑中添加的注意事项:我们不能将从
unique\u ptr.get()
获得的指针传递给任何可能保留副本的函数,这违背了
unique\u ptr
的全部要点。上面的代码并没有做到这一点。

首先,我觉得这个设计很有问题:您有一个多态层次结构,还有一个容器,其中包含该层次结构中特定成员的值。你挑起的问题没完没了。使用
std::vector
似乎更明智

无论如何,这里有一种适度安全且高效的方法,只将那些动态类型精确匹配的对象插入到容器中。它假定层次结构中的每个类都有一个可访问的副本构造函数

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

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

克隆人照它说的做。那么push_back会自动创建对象的副本吗?使用复制构造函数?执行
typeid(*object)==typeid(DerivedClass)
,会更快,不是吗?而且更安全,因为它不会对您的
EvenMoreDerivedClass
@Minion91进行切分。是的,容器包含值,它将使用复制构造函数存储对象的一个副本(在您的情况下,在其他一些情况下,它可能会在C++11中移动,或者就地构造……但无论如何,容器将保存传入的对象的自己的副本)@KerrekSB:我一般不喜欢使用
typeid
,因此它不会经常作为工具出现在脑海中。在这种情况下,由于切片的问题(如果是问题的话)也许值得使用
typeid
。关于性能,我的观点是,如果RTTI是一个性能瓶颈,甚至是显而易见的,那么应用程序的设计是错误的。@DavidRodríguez dribeas:但是我们已经在使用RTTI进行动态转换了!为什么要制作一个我们一开始就不想要的副本,而我们本可以知道这一点呢事先?我内心的某些东西(以及地下室的某个人)尖叫着“可怕的虐待”…如果你有一个
甚至更多的DerivedClass
?那会在副本中被分割…对于一个c++11问题:你真的应该使用
std::unique\u ptr
(或者
std::shared\u ptr
,这取决于你的使用场景)。由于内存泄漏的可能性(由于代码中缺少
delete
,或由于未处理引发异常的情况)相当高,因此不建议手动内存管理。因此,我的建议永远不会(通常会有一些例外,尽管不多)使用手动管理并始终使用smartpointers(或类似于boost Pointer Containers的东西)相反。@Grizzly根据Kerrek的第二个答案,我不再做手动内存管理。这一点并不是针对这种情况的,而是一条一般性的建议,即几乎总是最好不要使用手动内存管理。例如,在项目中,我正在研究一个
克隆
方法,返回一个原始指针不会产生任何效果它是通过代码检查完成的,因为这被认为是有风险的(忘记删除的可能性太高了,并且没有显示内存是如何实际分配的(是否
delete
甚至是正确的,或者析构函数调用之后是
free
或者甚至其他一些东西是正确的)@Grizzly我总是试图用
unique_ptr
来包装我的对象。不过我对c++11还是相当陌生(我来自c,我的最新项目是c89),有时我仍然在为新的移动语义而挣扎,但我已经做到了。在StackOverflow的另一个问题中建议使用克隆,没有人告诉我这是一个糟糕的做法(尽管我怀疑是这样的)使用get不是一种邪恶吗?它有点超过了一个独特的\u ptr no?@Minion91 no 1)如果它本身是邪恶的,它就不会在标准库中。2)这里我们没有泄漏指针(你可以
void someGetter(std:vector<DerivedClass> & returnV)
{
    if (typeid(*object) != typeid(DerivedClass)) { return; }

    returnV.insert(static_cast<DerivedClass&>(*object));
}
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);
    }
}