简体   繁体   English

什么时候对使用“new”创建的临时对象调用“delete”?

[英]When is 'delete' called on temporary objects created with `new`?

I initialise the SmartPtr class with new Time(1,0,0) .我用new Time(1,0,0)初始化 SmartPtr 类。

    //main.cpp 
    int main()
{
    SmartPtr pTime0(new Time(0,0,1));
}

Nowhere am I calling delete on new Time(1,0,0) .我没有在new Time(1,0,0)上调用 delete 。 Everything works fine, the program complies and runs.一切正常,程序符合并运行。 but I am confused -- where should/should not I delete Time(1,0,0) ?但我很困惑——我应该/不应该在哪里delete Time(1,0,0)

I do not understand the concept of temporary objects creating and deleting here.我不明白这里创建和删除临时对象的概念。 I know whenever I write new somewhere I MUST WRITE delete !我知道每当我在某个地方写new东西时,我必须写delete Could someone please explain where delete Time(1,0,0) takes place?有人可以解释一下delete Time(1,0,0)发生在哪里吗?

SmartPtr pTime0(new Time(0,0,1)) <-- new here returns a pointer to a newly allocated memory, and then in ctor I allocate new memory the second time?? SmartPtr pTime0(new Time(0,0,1)) <-- 这里的new返回一个指向新分配内存的指针,然后在 ctor 中我第二次分配new内存??

//SmartPtr.cpp 

SmartPtr::SmartPtr(Pointee * p):_pointee(new Pointee(*p))
{}

SmartPtr::~SmartPtr()
{
    delete _pointee; 
}

I don't know the details of your SmartPtr class.我不知道您的SmartPtr课程的详细信息。

In any case, if you have a constructor like this:无论如何,如果您有这样的构造函数:

 SmartPtr::SmartPtr(Pointee * p):_pointee(new Pointee(*p)) {}

and this is the destructor:这是析构函数:

 SmartPtr::~SmartPtr() { delete _pointee; }

then with this code:然后使用此代码:

 SmartPtr pTime0(new Time(0,0,1));

you leak one instance of Time(0,0,1) .您泄漏了Time(0,0,1)的一个实例。

In fact, you have one more new than delete (2 new s and 1 delete ):事实上,你比delete多了一个new (2 个new和 1 个delete ):

Step #1: You call new Time(0,0,1) and create a new object on the heap.第 1 步:调用new Time(0,0,1)并在堆上创建一个新对象。
( new count == 1) new计数 == 1)

Step #2: You pass this pointer to SmartPtr constructor, which deep copies previously created object and allocates a new copy on the heap, and keeps track of this copy via its _pointee data member.第 2 步:将此指针传递给SmartPtr构造函数,该构造函数深度复制先前创建的对象并在堆上分配一个新副本,并通过其_pointee数据成员跟踪该副本。
( new count == 2) new计数 == 2)

Step #3: When the SmartPtr destructor runs, it delete s the instance pointed by _pointee data member, but you leaked the firts Time(...) created on the heap with new Time(0,0,1) .第 3 步:当SmartPtr析构函数运行时,它会delete _pointee数据成员所指向的实例,但是您使用new Time(0,0,1)泄露了在堆上创建的第一个Time(...) ) 。
( delete count == 1; new count == 2) delete计数 == 1; new计数 == 2)

A possible fix for that could be to just have this constructor:一个可能的解决方法是只使用这个构造函数:

SmartPtr::SmartPtr(Pointee * p)
    : _pointee(p) // <--- transfer ownerhsip (no deep copies) !
{}

An easy way to identify potential leaks in these cases is to put some console tracing output in Time class constructors and destructor, and check that the trace output of destructor matches the ones of constructors, eg:在这些情况下识别潜在泄漏的一种简单方法是将一些控制台跟踪输出放在Time类构造函数和析构函数中,并检查析构函数的跟踪输出是否与构造函数匹配,例如:

Time::Time(....)
{
    // Do construction work....

    std::cout << "Time constructor\n";
}

Time::~Time(....)
{
    // Do destructor work....

    std::cout << "Time destructor\n";
}

The total count of "Time constructor" strings should match the total count of "Time destructor" strings. "Time constructor"字符串的总数应与"Time destructor"字符串的总数相匹配。

Two ways to fix:两种修复方法:

Method A, caller allocates, SmartPtr takes ownership:方法 A,调用者分配,SmartPtr 取得所有权:

SmartPtr::SmartPtr(Pointee * p):_pointee(p)
{
}

Method B, caller provides content and SmartPtr allocates:方法 B,调用者提供内容,SmartPtr 分配:

SmartPtr::SmartPtr(Pointee v):_pointee(new Pointee(std::move(v)))
{
}

And the destructor remains the same:析构函数保持不变:

SmartPtr::~SmartPtr()
{
    delete _pointee; 
}

The expression new Time(0,0,1) create a temporary pointer to a permanent object on the heap.表达式new Time(0,0,1)在堆上创建一个指向永久对象的临时指针。 The temporary pointer will indeed be destroyed automatically (which is a no-op), leaving the object still on the heap but unreferenced.临时指针确实会被自动销毁(这是一个无操作),使对象仍然在堆上但未被引用。 A leak has occurred.发生泄漏。

To prevent the leak, make sure to store the pointer somewhere and ensure that delete is eventually called on it.为防止泄漏,请确保将指针存储在某处并确保最终在其上调用 delete。

You can write your applications according to the principle never type new .你可以按照never type new的原则编写你的应用程序。
Combine this with existing smart-pointers, and it becomes:将它与现有的智能指针结合起来,它变成:

#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
}

And there will be no leak of anything ever.并且永远不会泄漏任何东西。

"Never type new" should apply to all application programming, with the only exception if you have to write a low level resource management library. “从不输入新”应该适用于所有应用程序编程,唯一的例外是您必须编写低级资源管理库。

Note that if a class does resource management, it should be it's sole function.请注意,如果一个类进行资源管理,它应该是它的唯一功能。

All your other classes should follow rule of zero您所有其他课程都应遵循零规则

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM