调用析构函数方法比较

时间:2010-12-13 06:24:50

标签: c++ malloc destructor new-operator placement-new

我只是想知道这三种调用析构函数的方法是否有任何重大/严重的区别。请考虑以下代码。另请考虑main()中提到的两种情况。

class Sample 
{
public:
    ~Sample()
    {
        cout << "destructor called" << endl;
    }
    void destroyApproach1() { this->~Sample(); }
    void destroyApproach2() { delete this; }
};

void destroyApproach3(Sample *_this)
{
    delete _this;
}

void TestUsingNew()
{
    Sample *pSample[] = { new Sample(), new Sample(),new Sample()};
    pSample[0]->destroyApproach1();
    pSample[1]->destroyApproach2();
    destroyApproach3(pSample[2]);
}
void TestUsingPlacementNew()
{
    void *buf1 = std::malloc(sizeof(Sample));
    void *buf2 = std::malloc(sizeof(Sample));
    void *buf3 = std::malloc(sizeof(Sample));
    Sample *pSample[3] = { new (buf1) Sample(), new (buf2) Sample(), new (buf3) Sample()};
    pSample[0]->destroyApproach1();
    pSample[1]->destroyApproach2();
    destroyApproach3(pSample[2]);
}
int main() 
{ 
    //Case 1 : when using new
    TestUsingNew();

    //Case 2 : when using placement new
    TestUsingPlacementNew();
    return 0;
}

请回复您正在回复的案例:案例1或案例2,或两者兼而有之!


此外,我试图以这种方式编写TestUsingPlacementNew(),但它正在抛出运行时异常(MSVC ++ 2008)。我不明白为什么:

void TestUsingPlacementNew()
{
    const int size = sizeof(Sample);
    char *buffer = (char*)std::malloc( size * 3);
    Sample *pSample[] = { new (buffer) Sample(), new (&buffer[size]) Sample(),new  (&buffer[2*size]) Sample()};
    pSample[0]->destroyApproach1();
    pSample[1]->destroyApproach2();
    destroyApproach3(pSample[2]);
}

也许,内存填充和/或对齐可能是原因?


相关主题:Destructor not called after destroying object placement-new'ed

2 个答案:

答案 0 :(得分:5)

是的,这些方法之间存在巨大差异:

  • destroyApproach1中,您只调用对象的析构函数;你实际上没有释放它占用的内存。

  • destroyApproach2destroyApproach3中,您调用对象的析构函数,释放对象占用的内存(使用delete表达)。在第一个TestUsingPlacementNew测试中,这两个都是错误的,因为对象占用的内存最初是通过调用malloc而不是new来分配的。

上次测试中发生运行时错误的原因是您尝试delete数组中索引为1的对象;最初不是通过调用new获得指向该元素的指针。在第一个例子中,它只“工作”(其中“工作”实际上意味着“行为未定义,但它似乎仍能正常运行),因为所有三个指针都是独立堆分配。”

答案 1 :(得分:1)

delete this不是在现代代码中调用析构函数的正确方法。一般来说,你不必调用析构函数:他们的魔力就是在适当的时候调用它们:

struct A {
    ~A() { std::cout << "running destructor\n"; }
};

int main()
{
    A a;
    return 0;
}
在所有情况下,

delete用于释放由new分配的内存。 delete this

时,new将导致问题失败
struct B {
    ~B() { delete this }
};

int main()
{
    B b;
    return 0;
}

这将使您的程序在几乎所有具有实际操作系统的平台上崩溃(“几乎全部”,因为这是技术上未定义的行为,并且允许符合标准的程序自行损坏并在这种情况下继续运行,保持手指交叉您的平台所做的不仅仅是让您在损坏的内存管理数据结构和无效堆栈中蹒跚而行。)


放置new主要用于硬件设备驱动程序或必须在特殊地址分配指针的其他位置。 In general you won't want to destroy objects allocated with placement new。如果您愿意,只需直接调用析构函数:

My_object* o = new(0xffff) My_object();
o->~My_object();

但是,请记住,Bjarne Stroustrup说,“应尽可能避免显式调用析构函数。偶尔,它们是必不可少的......新手应该在明确地调用析构函数之前考虑三次并且还要求更有经验的人同事之前“( C ++编程语言,10.4.11)。