удалить это поведение указателя в g ++ - PullRequest
2 голосов
/ 15 января 2010
#include <stdio.h>

class Foo {

        public:
                Foo(char x);
                Foo(char x, int y);
                ~Foo();
                void abc();
                void dev();
};

void Foo::dev()
{

        printf("inside dev \n");
}

void Foo::abc()
{

        printf("inside abc \n");
        delete this;
        dev();
}

Foo::Foo(char x)
{

      printf("inside 1 argu const---------------");

}

Foo::~Foo()
{

    printf("inside 1 argu dest---------------");
}

#include "test.h"

int main()
{

        Foo *obj=new Foo('a');
        printf("%u inside main\n", obj);
        obj->abc();
        return 0;
}

После просмотра результатов программы кажется, что функция "dev" все еще вызывается, несмотря на то, что "delete this" вызывается в функции abc перед вызовом dev? Как gcc / g ++ справляется с этим?

Ответы [ 7 ]

12 голосов
/ 15 января 2010

Объект может быть доступен в течение неопределенного времени. Кроме того, delete не влияет на рассматриваемый указатель.

Delete просто вызывает деструктор для экземпляра объекта. Delete возвращает память в пул, но она не определена (и связана со временем выполнения) относительно того, когда эта память будет использоваться повторно (если вообще будет). Объект вполне может быть доступен до конца программы, но суть в следующем: не рассчитывайте на него .

Существует не столь очевидная ловушка, на которую следует обратить внимание: объект не может знать, был ли он выделен динамически. Следовательно, если объект был статически размещен при вызове, delete this на указанном объекте окажется проблематичным. Это не тот случай, описанный выше.

6 голосов
/ 15 января 2010

Удалить просто освобождает память (также вызывая деструктор). По сути, вы вызывали dev с указателем trash this, он работает только потому, что dev не был виртуальным и не пытался получить доступ к каким-либо переменным-членам, иначе он мог бы получить доступ к нарушению, как и любой другой недопустимый указатель.

2 голосов
/ 15 января 2010

Как gcc / g ++ справляется с этим?

Он обрабатывает это, как вы можете видеть из ваших испытаний.

Хотя это рискованно: например, если вы изменили метод dev для доступа к любым данным члена экземпляра класса Foo, то ваше поведение (т. Е. Вызов метода dev после удаления экземпляра Foo) будет недопустимым. и поведение будет неопределенным: фактическое поведение будет варьироваться в зависимости от того, что происходило в другом месте программы, например, от того, была ли перераспределена память, которую занимал экземпляр Foo (и которая была освобождена при удалении экземпляра Foo) другая тема.

Поведение также было бы другим, если бы метод dev был виртуальным, а Foo был базовым классом или подклассом в иерархии наследования.

Было бы лучше, если бы вы определили dev как статический метод:

class Foo {

public:
    Foo(char x);
    Foo(char x, int y);
    ~Foo();
    void abc();
    static void dev();
};

Если вы вызываете функцию, которая не может быть определена как статическая (потому что она виртуальная или потому что она обращается к данным члена экземпляра), то делать то, что вы делали, было бы незаконно.

1 голос
/ 15 января 2010

Удалить не удаляет код или статические переменные класса, только переменные экземпляра. Однако, как отмечали другие люди, поведение, которое вы получаете, используя указатель объекта после его удаления, не определено.

Но спросите себя об этом (или, возможно, спросите stackoverflow ;-): Можете ли вы вызвать статическую функцию-член класса, если экземпляров класса не существует? (Ответ, конечно, да.)

Если бы dev () был статическим, этот код был бы совершенно законным.

1 голос
/ 15 января 2010

delete this обычно не влияет на сам указатель this, поэтому его все равно можно использовать для вызова функции. Это поведение не определено, хотя - оно может работать, а может и нет. В общем, delete this - плохая идея в C ++. Единственное оправдание его использования - в некоторых классах с подсчетом ссылок, и существуют более подходящие методы подсчета ссылок, которые не требуют его использования.

1 голос
/ 15 января 2010

Я бы ожидал, что dev(); будет вызываться в этот момент, но это неопределенное поведение, так как указатель this указывает на объект, который был уничтожен. Причина, по которой ваш звонок кажется успешным, заключается в том, что вам повезло, и ничто иное не требовало памяти, на которую указывает this, когда вы звоните dev(), иначе результаты были бы «интересными», скажем так. наименее.

0 голосов
/ 15 января 2010

Все, что делает delete, вызывает деструктор, затем operator delete (встроенная версия, если в вашем классе нет переопределения). Это не умнее, чем free(), правда. Ничто не мешает вам использовать удаленный объект. Конечно, это не сработает.

Звонить delete this; тоже довольно рискованно. С этого момента объект не будет действительным, и если вы попытаетесь вызвать методы или получить доступ к членам, вы окажетесь на территории неопределенной операции.

...