【问题标题】:Invoking Destructor Approach Comparison调用析构方法比较
【发布时间】:2010-12-13 06:24:49
【问题描述】:

我只是想知道这三种调用析构函数的方法是否有任何显着/严重的差异。考虑以下代码。也请考虑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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 第二段是针对使用placement new时的第二种情况?
    • @Nawaz:我已经更新了答案以表明我试图回答的部分。
    【解决方案2】:

    delete this 不是在现代代码中调用析构函数的正确方法。一般来说,您不必调用析构函数:它们的神奇之处在于它们在适当的时间被调用:

    struct A {
        ~A() { std::cout << "running destructor\n"; }
    };
    
    int main()
    {
        A a;
        return 0;
    }
    

    delete 在所有情况下都用于释放new 分配的内存。当对象未被new 分配时,delete this 将导致麻烦:

    struct B {
        ~B() { delete this }
    };
    
    int main()
    {
        B b;
        return 0;
    }
    

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


    Placement 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)。

    【讨论】:

      猜你喜欢
      • 2018-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多