首页 > 试题广场 >

以下哪些做法是不正确或者应该极力避免的?

[不定项选择题]
【多选】以下哪些做法是不正确或者应该极力避免的:( )
  • 构造函数声明为虚函数
  • 派生关系中的基类析构函数声明为虚函数
  • 构造函数中调用虚函数
  • 析构函数中调用虚函数
推荐
答案:A C D
解释:
所谓虚函数就是多态情况下只执行一个,而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象,如果构造函数设为虚函数,那么当你在构造父类的构造函数时就不得不显示的调用构造,还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错.
在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。
在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。


编辑于 2015-01-07 14:50:05 回复(16)
class AA
{
public:
AA(int aa = 0)
{
a = aa;
print();
}
virtual void print()
{
printf("parent\n");
}
~AA(){}
private:
int a;
};
class BB :AA
{
public:
BB():AA(1){}
~BB(){}

virtual void print()
{
printf("child\n");
}
};

void main()
{
AA a;
BB b;    
system("pause");
}
在父类的构造函数调用虚函数print(),结果如下:

因为vptr指针的初始化是分步完成的:
1、当指向父类的构造函数的时候,C++编译器会初始化子类的vptr指针,让vptr指针指向父类的虚函数表
2、当父类的构造函数执行完毕以后,再执行子类的构造函数,这个时候,让vptr指针真正的指向子类的虚函数表
编辑于 2017-06-30 14:58:02 回复(0)
先析构子类再析构父类,如果父类析构函数有虚函数,会导致调用子类的已经析构的内容。

先构造父亲类再构造子类,如果父类构造函数有虚函数,会导致调用子类还没构造的内容。
发表于 2016-06-19 22:11:56 回复(9)

A:构造函数不能声明为虚函数的原因是:
1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。

2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。


发表于 2015-10-26 21:22:13 回复(0)
 1.构造函数不能是虚函数,创建派生类对象时,将调用派生类的构造函数,不是基类的构造函数。然后派生类的构造函数将使用基类的构造函数,这种顺序不同于继承机制。因此,派生类不继承基类的构造函数,所以没意义。
     2.构造函数中不能调用虚函数。创建一个派生类对象将调用派生类的构造函数,派生类的构造函数将先使用基类的构造函数,如果基类的构造函数中使用虚函数,将会调用未初始化的派生类。
     3.析构函数中调用虚函数。析构的顺序是先析构子类,在析构基类,如果基类的析构函数中使用了虚函数,将会调用已经析构了的子类中的函数。

发表于 2018-05-30 11:02:14 回复(2)
       1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
        2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
        3. 构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
        4. 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有必要成为虚函数。
        5. 当一个构造函数被调用时,它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的 VTABLE,等.直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生类顺序的另一个理由。但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将只产生通过它自己的VTABLE的调用,而不是最后的VTABLE(所有构造函数被调用后才会有最后的VTABLE)
-----------------------------------
构造函数为什么不能是虚函数
https://blog.51cto.com/lihaichuan/1294011
发表于 2022-08-15 13:15:55 回复(0)
发表于 2018-10-10 11:59:09 回复(1)
对于A选项,若将构造函数声明为virtual,编译器会报错 “constructor cannot be declared 'virtual' ”。
发表于 2015-10-02 10:09:09 回复(0)

A. 构造函数声明为虚函数:构造函数不能声明为虚函数,因为在对象的构造过程中,虚函数表还未被完全构建,无法正确调用虚函数。

C. 构造函数中调用虚函数:在构造函数中调用虚函数可能导致意想不到的结果,因为在派生类对象的构造过程中,基类部分的成员尚未初始化,虚函数的行为可能会出现不一致。

D. 析构函数中调用虚函数:析构函数调用虚函数是危险的,因为在派生类对象的析构过程中,派生类部分的成员可能已经被销毁,此时调用虚函数可能导致访问已经被释放的内存。

因此,选项 A、C、D 是不正确或者应该极力避免的做法。选项 B 是正确的做法,派生关系中的基类析构函数应当声明为虚函数,以确保在删除指向派生类对象的基类指针时,能够正确调用派生类的析构函数。

编辑于 2023-11-29 20:06:31 回复(0)
大家基本上都是说,构造,析构不能带有虚函数,避免调用未构建的或者已经析构内容,调用出错这道理能理解,但是为啥b不选呢?它不是错的吗?基类析构调用已经析构的子类明显会错。。。。
发表于 2021-01-18 08:52:30 回复(1)
CD effective c++ item9
发表于 2020-11-05 09:36:27 回复(0)
总之与构造函数有关的,都不能声明为虚函数
发表于 2017-09-22 11:15:28 回复(0)
膝构函数会先删子类,如果父类有虚函数,无法完成对子类调用!!!!
发表于 2016-08-01 09:24:26 回复(1)