Сравнение вызывающего деструктора - PullRequest
0 голосов
/ 13 декабря 2010

Мне просто любопытно узнать, есть ли существенная / серьезная разница в этих трех подходах вызова деструктора. Рассмотрим следующий код. Пожалуйста, рассмотрите также два случая, упомянутых в 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]);
}

Может быть, причиной может быть заполнение памяти и / или выравнивание?


Тема по теме: Деструктор не вызывается после уничтожения объекта размещения-new'ed

Ответы [ 2 ]

5 голосов
/ 13 декабря 2010

Да, между этими подходами существует огромная разница:

  • В destroyApproach1 вы вызываете только деструктор объекта; вы на самом деле не освобождаете память, которую он занимал.

  • В destroyApproach2 и destroyApproach3 вы вызываете деструктор объекта , а вы освобождаете память, занятую объектом (используя выражение delete). В первом тесте TestUsingPlacementNew оба они также неверны, поскольку память, занятая объектом, изначально была выделена вызовом malloc, а не new.

Ошибка времени выполнения в вашем последнем тесте возникает из-за попытки delete объекта с индексом 1 в массиве; указатель на этот элемент изначально не был получен при вызове new. В первом примере это только «работает» (где «работает» на самом деле означает «поведение не определено, но оно все еще функционирует правильно), потому что все три указателя на независимые выделения кучи.

1 голос
/ 13 декабря 2010

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 в основном предназначено для драйверов устройств или других мест, где указатель должен быть размещен по специальному адресу. Как правило, вы не хотите уничтожать объекты, размещенные при размещении new. В тех случаях, когда вы хотите, просто позвоните деструктору напрямую:

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

Но, помните, Бьярн Страуструп сказал: «Следует по возможности избегать явных вызовов деструкторов. Иногда они необходимы. коллега, прежде чем сделать это "( Язык программирования C ++ , 10.4.11).

...