在我的应用程序中,我正在创建并返回一个数组,该数组填充了派生类中动态分配的对象,如下所示:
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 :(得分: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));
}
这与你的语义略有不同,因为你的代码允许*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);
}
}
答案 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
的整个点。上面的代码没有这样做。