2

我用 . 初始化 SmartPtr 类new Time(1,0,0)

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

我没有在任何地方调用 delete on new Time(1,0,0)。一切正常,程序符合并运行。但我很困惑——我应该/不应该在哪里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; 
}
4

4 回答 4

2

我不知道你SmartPtr上课的细节。

无论如何,如果您有这样的构造函数:

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

这是析构函数:

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

然后使用此代码:

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

你泄露了一个Time(0,0,1).

事实上,你有一个new多于delete(2 news 和 1 delete):

第 1 步:new Time(0,0,1)在堆上调用并创建一个新对象。
new计数 == 1)

步骤#2:将此指针传递给SmartPtr构造函数,该构造函数深度复制_pointee先前创建的对象并在堆上分配一个新副本,并通过其数据成员跟踪该副本。
new计数 == 2)

步骤#3:当SmartPtr析构函数运行时,它是数据成员delete指向的实例_pointee,但是你泄露了在堆上Time(...)创建的new Time(0,0,1).
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"

于 2013-09-25T18:32:55.747 回答
1

两种修复方法:

方法 A,调用者分配,SmartPtr 取得所有权:

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

方法 B,调用者提供内容,SmartPtr 分配:

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

析构函数保持不变:

SmartPtr::~SmartPtr()
{
    delete _pointee; 
}
于 2013-09-25T19:29:29.150 回答
0

该表达式new Time(0,0,1)创建一个指向堆上永久对象的临时指针。临时指针确实会被自动销毁(这是一个无操作),使对象仍然在堆上但未被引用。发生泄漏。

为防止泄漏,请确保将指针存储在某处并确保最终在其上调用 delete。

于 2013-09-25T18:27:35.320 回答
0

你可以按照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
}

并且永远不会泄漏任何东西。

“从不输入新”应该适用于所有应用程序编程,唯一的例外是您必须编写低级资源管理库。

请注意,如果一个类进行资源管理,它应该是它的唯一功能。

您所有其他课程都应遵循零规则

于 2015-08-25T22:36:47.893 回答