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;
}