[英]Temporary objects - when are they created, how do you recognise them in code?
[英]When is 'delete' called on temporary objects created with `new`?
我用new Time(1,0,0)
初始化 SmartPtr 类。
//main.cpp
int main()
{
SmartPtr pTime0(new Time(0,0,1));
}
我没有在new Time(1,0,0)
上调用 delete 。 一切正常,程序符合并运行。 但我很困惑——我应该/不应该在哪里delete Time(1,0,0)
?
我不明白这里创建和删除临时对象的概念。 我知道每当我在某个地方写new
东西时,我必须写delete
! 有人可以解释一下delete Time(1,0,0)
发生在哪里吗?
SmartPtr pTime0(new Time(0,0,1)) <-- 这里的new
返回一个指向新分配内存的指针,然后在 ctor 中我第二次分配new
内存??
//SmartPtr.cpp
SmartPtr::SmartPtr(Pointee * p):_pointee(new Pointee(*p))
{}
SmartPtr::~SmartPtr()
{
delete _pointee;
}
我不知道您的SmartPtr
课程的详细信息。
无论如何,如果您有这样的构造函数:
SmartPtr::SmartPtr(Pointee * p):_pointee(new Pointee(*p)) {}
这是析构函数:
SmartPtr::~SmartPtr() { delete _pointee; }
然后使用此代码:
SmartPtr pTime0(new Time(0,0,1));
您泄漏了Time(0,0,1)
的一个实例。
事实上,你比delete
多了一个new
(2 个new
和 1 个delete
):
第 1 步:调用new Time(0,0,1)
并在堆上创建一个新对象。
( new
计数 == 1)
第 2 步:将此指针传递给SmartPtr
构造函数,该构造函数深度复制先前创建的对象并在堆上分配一个新副本,并通过其_pointee
数据成员跟踪该副本。
( new
计数 == 2)
第 3 步:当SmartPtr
析构函数运行时,它会delete
_pointee
数据成员所指向的实例,但是您使用new Time(0,0,1)
泄露了在堆上创建的第一个Time(...)
) 。
( delete
计数 == 1; new
计数 == 2)
一个可能的解决方法是只使用这个构造函数:
SmartPtr::SmartPtr(Pointee * p)
: _pointee(p) // <--- transfer ownerhsip (no deep copies) !
{}
在这些情况下识别潜在泄漏的一种简单方法是将一些控制台跟踪输出放在Time
类构造函数和析构函数中,并检查析构函数的跟踪输出是否与构造函数匹配,例如:
Time::Time(....)
{
// Do construction work....
std::cout << "Time constructor\n";
}
Time::~Time(....)
{
// Do destructor work....
std::cout << "Time destructor\n";
}
"Time constructor"
字符串的总数应与"Time destructor"
字符串的总数相匹配。
两种修复方法:
方法 A,调用者分配,SmartPtr 取得所有权:
SmartPtr::SmartPtr(Pointee * p):_pointee(p)
{
}
方法 B,调用者提供内容,SmartPtr 分配:
SmartPtr::SmartPtr(Pointee v):_pointee(new Pointee(std::move(v)))
{
}
析构函数保持不变:
SmartPtr::~SmartPtr()
{
delete _pointee;
}
表达式new Time(0,0,1)
在堆上创建一个指向永久对象的临时指针。 临时指针确实会被自动销毁(这是一个无操作),使对象仍然在堆上但未被引用。 发生泄漏。
为防止泄漏,请确保将指针存储在某处并确保最终在其上调用 delete。
你可以按照never type new的原则编写你的应用程序。
将它与现有的智能指针结合起来,它变成:
#include <memory> // this is where the smart-pointers live
#include "Time.h" // or whatever header defines your "Time" class
int main()
{
// note that make_shared is essentially a forwarding constructor,
// give it whatever parameters Time's constructor would take
auto p = std::make_shared<Time>(0,0,1);
// use p here
}
并且永远不会泄漏任何东西。
“从不输入新”应该适用于所有应用程序编程,唯一的例外是您必须编写低级资源管理库。
请注意,如果一个类进行资源管理,它应该是它的唯一功能。
您所有其他课程都应遵循零规则
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.