где использовать объектный класс в Java?

Я подумал, что было бы полезно обсудить «неопределенное» поведение или, по крайней мере, неопределенное поведение «сбой», которое может возникнуть при удалении через базовый класс (/ struct) без виртуального деструктора или, точнее, vtable. В приведенном ниже коде перечислены несколько простых структур (то же самое было бы верно для классов).

#include <iostream>
using namespace std;

struct a
{
    ~a() {}

    unsigned long long i;
};

struct b : a
{
    ~b() {}

    unsigned long long j;
};

struct c : b
{
    ~c() {}

    virtual void m3() {}

    unsigned long long k;
};

struct d : c
{
    ~d() {}

    virtual void m4() {}

    unsigned long long l;
};

int main()
{
    cout << "sizeof(a): " << sizeof(a) << endl;
    cout << "sizeof(b): " << sizeof(b) << endl;
    cout << "sizeof(c): " << sizeof(c) << endl;
    cout << "sizeof(d): " << sizeof(d) << endl;

    // No issue.

    a* a1 = new a();
    cout << "a1: " << a1 << endl;
    delete a1;

    // No issue.

    b* b1 = new b();
    cout << "b1: " << b1 << endl;
    cout << "(a*) b1: " << (a*) b1 << endl;
    delete b1;

    // No issue.

    c* c1 = new c();
    cout << "c1: " << c1 << endl;
    cout << "(b*) c1: " << (b*) c1 << endl;
    cout << "(a*) c1: " << (a*) c1 << endl;
    delete c1;

    // No issue.

    d* d1 = new d();
    cout << "d1: " << d1 << endl;
    cout << "(c*) d1: " << (c*) d1 << endl;
    cout << "(b*) d1: " << (b*) d1 << endl;
    cout << "(a*) d1: " << (a*) d1 << endl;
    delete d1;

    // Doesn't crash, but may not produce the results you want.

    c1 = (c*) new d();
    delete c1;

    // Crashes due to passing an invalid address to the method which
    // frees the memory.

    d1 = new d();
    b1 = (b*) d1;
    cout << "d1: " << d1 << endl;
    cout << "b1: " << b1 << endl;
    delete b1;  

/*

    // This is similar to what's happening above in the "crash" case.

    char* buf = new char[32];
    cout << "buf: " << (void*) buf << endl;
    buf += 8;
    cout << "buf after adding 8: " << (void*) buf << endl;
    delete buf;
*/
}

Я не предлагаю, нужны ли вам виртуальные деструкторы или нет, хотя я думаю, что в целом это хорошая практика иметь их. Я просто указываю причину, по которой вы можете столкнуться с сбоем, если ваш базовый класс (/ struct) не имеет vtable и ваш производный класс (/ struct) делает, и вы удаляете объект через базовый класс (/ struct) указатель. В этом случае адрес, который вы передаете в бесплатную процедуру кучи, является недопустимым и, следовательно, причиной сбоя.

Если вы запустите указанный выше код, вы увидите, когда проблема возникнет. Когда этот указатель базового класса (/ struct) отличается от этого указателя производного класса (/ struct), вы столкнетесь с этой проблемой. В приведенном выше примере структуры a и b не имеют vtables. У структур c и d есть vtables. Таким образом, a или b указатель на экземпляр объекта c или d будут исправлены для учета vtable. Если вы передадите этот указатель a или b для его удаления, произойдет сбой из-за того, что адрес недействителен для бесплатной процедуры кучи.

Если вы планируете удалять производные экземпляры, имеющие vtables из указателей базового класса, вам нужно убедитесь, что базовый класс имеет таблицу vtable. Один из способов сделать это - добавить виртуальный деструктор, который вы, возможно, захотите правильно очистить ресурсы.

0
задан Sumit Singh 5 September 2012 в 12:44
поделиться