不能是虚函数的几类函数:{\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{为什么构造函数不可以是虚函数,而析构函数最好是虚函数?}}为什么构造函数不可以是虚函数,而析构函数最好是虚函数?
-
从内存角度:虚函数通过虚表实现,但类在调用构造函数前并没有实例化,也就没有虚表,所以矛盾。
-
从使用角度:构造函数是对象创建时自主调用的,并不是通过父类指针调用,虚构造没有意义。
-
从实现角度,虚函数实现继承链上从上至下的多态,而构造函数是继承链上从下至上调用,两者实现上矛盾。
-
从意义角度:虚函数是为了解决析构函数内存泄漏,构造不会内存泄漏。