Удалить это поведение указателя в g++

#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++ справляется с этим?

Ответы

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

Он обрабатывает его, как вы можете видеть из вашего тестирования.

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

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

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

class Foo {

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Вызов delete this; также довольно рискован. Объект не будет действителен с этой точки, и если вы попытаетесь вызвать методы или получить доступ к элементам, вы находитесь в области undefined -operation.