C++ 通过对象指针调用析构函数

C++ 通过对象指针调用析构函数,c++,vtable,virtual-destructor,C++,Vtable,Virtual Destructor,您能解释一下为什么下面的代码没有崩溃,以及如何处理它以避免崩溃吗 class Base { public: Base() {cout << "Base constr" << endl;} virtual void func() {cout << "Base func()" << endl;} virtual ~Base() {cout << "Base destr" << endl;} }; cla

您能解释一下为什么下面的代码没有崩溃,以及如何处理它以避免崩溃吗

class Base
{
public:
    Base() {cout << "Base constr" << endl;}
    virtual void func() {cout << "Base func()" << endl;}
    virtual ~Base() {cout << "Base destr" << endl;}
};

class Layer1 : public Base
{
public:
    Layer1() {cout << "Layer1 constr" << endl;}
    virtual void func() {cout << "Layer1 func()" << endl;}
    virtual ~Layer1() {cout << "Layer1 destr" << endl;}
};

class Layer2 : public Layer1
{
public:
    Layer2() {cout << "Layer2 constr" << endl;}
    virtual void func() {cout << "Layer2 func()" << endl;}
    ~Layer2() {cout << "Layer2 destr" << endl;}
};

int main(int argc, char** argv)
{
    Layer2 * l2ptr = (Layer2 *) new Base;
    l2ptr->func();
    delete l2ptr;

    return 0;
}
我的意思是在调用deletel2ptr的地方。从第一眼看,似乎应该调用
Layer2
析构函数,但没有创建
Layer2
对象。另外,
Layer2
析构函数不是
virtual
。当我将其设置为虚拟时,输出是相同的。为什么?

还有下一个问题:关于通过父类对象的子类指针访问父类对象,有哪些问题和注意事项?

编辑: 如果我将第2层
类更改为此

class Layer2 : public Layer1
{
public:
    Layer2() {cout << "Layer2 constr" << endl;}
    virtual void func() {cout << "Layer2 func()" << endl;}
    void func2() {cout << "Layer2 func2()" << endl;}
    virtual ~Layer2() {cout << "Layer2 destr" << endl;}
};
它仍然有效,输出为:

Base constr
Base func()
Layer2 func2()
Base destr

同样,为什么?

您的代码有未定义的行为,正如前面指出的那样-例如,当您通过指向
Layer2
的指针访问
Base
对象时

未定义的行为包括看似有效(以及其他任何行为)。在这种情况下,对象的布局非常相似,不会出现太严重的错误。vtable指针位于同一位置;对虚拟函数的调用正确地转到
Base
版本,因为对象是
Base
并且包含指向
Base
vtable的指针。非虚拟函数将转到您告诉编译器对象的类型

(实现不必使用vtable,但通常会使用。)


但这都是未定义的行为;不要这样做,最明显的是不要依赖它。

您的代码具有未定义的行为,正如前面所指出的那样-例如,当您通过指向
层2
的指针访问
基本对象时

未定义的行为包括看似有效(以及其他任何行为)。在这种情况下,对象的布局非常相似,不会出现太严重的错误。vtable指针位于同一位置;对虚拟函数的调用正确地转到
Base
版本,因为对象是
Base
并且包含指向
Base
vtable的指针。非虚拟函数将转到您告诉编译器对象的类型

(实现不必使用vtable,但通常会使用。)


但这都是未定义的行为;不要这样做,最明显的是不要依赖它。

您的代码有未定义的行为,因此几乎任何关于它为什么做任何事情的问题都是毫无意义和无法回答的。从某种程度上说,它做任何特定的事情,基本上只是运气

你的东西基本上与你可能想要的东西(或者你在任何情况下都应该想要的东西)背道而驰,在这个顺序上会有一个
main
东西:

int main(int argc, char** argv)
{
    Base *bptr = new Layer2;
    bptr->func();       

    Layer2 *l2ptr = dynamic_cast<Layer2 *>(bptr);

    if (l2ptr)
        l2ptr->func2();

    delete bptr;
}
int main(int argc,char**argv)
{
基本*bptr=新层2;
bptr->func();
第2层*l2ptr=动态投影(bptr);
if(l2ptr)
l2ptr->func2();
删除bptr;
}
在这里,我们使用指向Base的
指针来引用类型为
Layer2
的对象,而不是相反。由于Layer2是从
Base
派生的,因此这是允许的,并且行为是有意义的

这让我们来谈谈它为什么会这样做的细节

Layer2
中未标记为
virtual
的析构函数基本上是不相关的:因为它在基类中标记为
virtual
,所以在所有派生类中仍然是
virtual

C++标准,§[class.virtual]/2:

如果在类基类中声明了虚拟成员函数vf,并且在派生类、直接或间接从基类派生的类中声明了与Base::vf具有相同名称、参数类型列表(8.3.5)、cv限定符和ref限定符(或没有相同的)的成员函数vf,则派生::vf也是虚拟的(无论是否如此声明)它覆盖了111 Base::vf

C++标准,§[class.virtual]/6:

即使析构函数没有被继承,派生类中的析构函数也会重写声明为虚拟的基类析构函数


您的代码有未定义的行为,所以几乎任何关于它为什么做任何事情的问题都是毫无意义和无法回答的。从某种程度上说,它做任何特定的事情,基本上只是运气

你的东西基本上与你可能想要的东西(或者你在任何情况下都应该想要的东西)背道而驰,在这个顺序上会有一个
main
东西:

int main(int argc, char** argv)
{
    Base *bptr = new Layer2;
    bptr->func();       

    Layer2 *l2ptr = dynamic_cast<Layer2 *>(bptr);

    if (l2ptr)
        l2ptr->func2();

    delete bptr;
}
int main(int argc,char**argv)
{
基本*bptr=新层2;
bptr->func();
第2层*l2ptr=动态投影(bptr);
if(l2ptr)
l2ptr->func2();
删除bptr;
}
在这里,我们使用指向Base的
指针来引用类型为
Layer2
的对象,而不是相反。由于Layer2是从
Base
派生的,因此这是允许的,并且行为是有意义的

这让我们来谈谈它为什么会这样做的细节

Layer2
中未标记为
virtual
的析构函数基本上是不相关的:因为它在基类中标记为
virtual
,所以在所有派生类中仍然是
virtual

C++标准,§[class.virtual]/2:

如果在类基类中声明了虚拟成员函数vf,并且在派生类、直接或间接从基类派生的类中声明了与Base::vf具有相同名称、参数类型列表(8.3.5)、cv限定符和ref限定符(或没有相同的)的成员函数vf,则派生::vf也是虚拟的(无论是否如此声明)它覆盖了111 Base::vf

C++标准,§[class.virtual]/6:

即使析构函数没有被继承,派生类中的析构函数也会重写声明为虚拟的基类析构函数


如果我错了,请纠正我,但您从不构造
Layer2
Layer1
的对象;只有一个
Base
对象(请注意,是C风格的,不是C++)被强制转换为指向
Layer2
类型的指针。那个
int main(int argc, char** argv)
{
    Base *bptr = new Layer2;
    bptr->func();       

    Layer2 *l2ptr = dynamic_cast<Layer2 *>(bptr);

    if (l2ptr)
        l2ptr->func2();

    delete bptr;
}