不 能 是 虚 函 数 的 几 类 函 数 : {\red{不能是虚函数的几类函数:}} 不能是虚函数的几类函数:
1
−
构
造
函
数
{\green{1-构造函数}}
1−构造函数
2
−
内
联
函
数
{\green{2-内联函数}}
2−内联函数
3
−
静
态
函
数
{\green{3-静态函数}}
3−静态函数
4
−
友
元
函
数
{\green{4-友元函数}}
4−友元函数
5
−
普
通
非
类
成
员
函
数
{\green{5-普通非类成员函数}}
5−普通非类成员函数
构 造 函 数 与 虚 函 数 顺 序 : {\red{构造函数与虚函数顺序:}} 构造函数与虚函数顺序:
class A{
public:
A(){a();}
virtual void a(){
cout<<"A"<<endl;
}
};
class B : public A{
public:
virtual void a(){
cout<<"B"<<endl;
}
};
int main(){
B b;
return 0;
}
输
出
"
A
"
{\orange{输出"A"}}
输出"A"
main函数中实例化b,调用B构造函数,若构造函数有参数则初始化参数。
因为B继承了A,调用A构造函数,若构造函数有参数则初始化参数。
因为A()调用了a(),执行A中函数a(),此时虚机制不起作用,不会执行B中的函数b(),
因为B构造函数未完成,可能存在未初始化成员变量,按逻辑编译器也不会让虚机制生效。
A::a()完毕后退出函数栈,A构造函数完毕后退出函数栈,B构造函数完毕后退出函数栈。
虚 函 数 与 参 数 : {\red{虚函数与参数:}} 虚函数与参数:
class A{
public:
virtual void print(int i = 10){
cout<<"A "<<i<<endl;
}
};
class B:public A{
public:
virtual void print(int i = 20){
cout<<"B "<<i<<endl;
}
};
int main(){
A a;
B b;
a.print();
b.print();
A *x,*y;
x = &a;
y = &b;
x->print();
y->print();
return 0;
}
输 出 : “ A 10 B 20 A 10 B 10 " {\orange{输出:“A\ 10\ B\ 20\ A\ 10\ B\ 10"}} 输出:“A 10 B 20 A 10 B 10"
前两组不解释。
第三组,当基类指针指向基类实例,则虚机制不会生效。
第四组,当基类指针指向派生类实例,则虚机制生效,
此时,由于是预定义参数,则先初始化参数,再执行对应虚函数,具体原因我也不清楚。
继 承 与 隐 式 转 换 : {\red{继承与隐式转换:}} 继承与隐式转换:
- 位于继承链前的类指针可以指向继承链后的类实例,反之不可以。
- 位于继承链后的类实例可以隐式转换为继承链前的类实例,反之不可以。
为 什 么 构 造 函 数 不 可 以 是 虚 函 数 , 而 析 构 函 数 最 好 是 虚 函 数 ? {\red{为什么构造函数不可以是虚函数,而析构函数最好是虚函数?}} 为什么构造函数不可以是虚函数,而析构函数最好是虚函数?
-
从内存角度:虚函数通过虚表实现,但类在调用构造函数前并没有实例化,也就没有虚表,所以矛盾。
-
从使用角度:构造函数是对象创建时自主调用的,并不是通过父类指针调用,虚构造没有意义。
-
从实现角度,虚函数实现继承链上从上至下的多态,而构造函数是继承链上从下至上调用,两者实现上矛盾。
-
从意义角度:虚函数是为了解决析构函数内存泄漏,构造不会内存泄漏。