/*
虚函数要求:
1虚函数能够产生虚函数地址,并存储到vftable中
2必须存在对象,这样才能通过vfptr找到vftable中的虚函数地址
是否虚函数的调用一定是动态绑定?
不是的,如果设置为虚构造函数,则不会发生动态绑定,而是静态绑定
并且,如果不是使用指针/引用去调用虚函数,发生的也是静态绑定
重载、隐藏、覆盖区分:
重载:函数名相同,参数列表不同,不关心返回值
隐藏:在继承结构中,子类的同名成员可以隐藏父类的同名成员
注意子的fun()可以隐藏父的fun(int),只要是同名就可以隐藏
原因是:对象会优先找自己作用域里的同名成员,没有的话才回去父类中找
即使子类中找到但不合条件也会报错
覆盖/重写:只变动内容,如果子类重写了父类虚函数,则子类成员函数也为虚函数
在继承中,父类指针指向子类对象,该指针调用同名覆盖方法(vftable中),因此可以调用子类方法
重载和重写都可以实现多态,前者在编译时实现,后者在运行时实现
https://blog.csdn.net/wy1511861159/article/details/115464779
*/
class base
{
private:
int fu;
public:
base(int val) :fu(val) {}
// 父类只要有virtual,就一定生成4字节的vfptr指针
virtual void funa() { cout << "父类的funA()" << endl; }
virtual void funb() { cout << "父类的funB()" << endl; }
void func(int i = 10) { cout << "父类的funC()" << endl; }
};
class derive :public base
{
private:
int zi;
public:
derive(int val) :base(val), zi(val) {}
// 只要子类函数被判定为虚函数,就一定生成4字节的vfptr指针
void funa() { cout << "子类的funA()" << endl; } // 满足virtual条件,覆盖父类的funa()
void func() { cout << "子类的funC()" << endl; } // 只是重写了父类的成员函数,不算多态
};
int main()
{
derive a(10);
base* p = &a; // 父类指针指向子类对象
a.funa(); // 子类隐藏了父类的同名虚函数
a.base::funa(); // 只有加了作用域才能调用被隐藏的虚函数
a.func(); // 子类隐藏了父类的同名函数
a.base::func(20); // 只有加了作用域才能调用被隐藏的虚函数
//a.func(20); // 错误,因为父类的func(int)被隐藏了无法直接调用
cout << "============================================================" << endl;
cout << "父类大小:" << sizeof(base) << endl; // 8,int的4和自己vfptr的4
cout << "子类大小:" << sizeof(derive) << endl; // 12,父类int的4,自己int的4和自己vfptr的4
cout << "指针类型:\t\t" << typeid(p).name() << endl; // base*,静态绑定,类型在编译时期决定
cout << "指针指向地址的类型:\t" << typeid(*p).name() << endl; // derive,动态绑定,取决于指针指向什么类型
// 如果base没有virtual,那么*p取决于编译时期的类型
// 如果base有virtual,*p取决于运行时期的类型(RTTI类型)(指向了何种类型的对象)
cout << "============================================================" << endl;
p->funa(); // p指针指向子类,并且funa()为虚函数,所以调用子类funa()(多态)
p->funb(); // 子类没重写funb(),所以调用父类继承过来的funb()
p->func(); // 静态绑定,调用函数取决于编译期间p的类型,所以调用父类func()
cout << "============================================================" << endl;
derive b(20);
base& p1 = b; // 引用等同指针,都会发生动态绑定
p1.funa(); // 动态绑定,指向子类函数
p1.func(); // 静态绑定,指向父类函数
base c(20);
//derive* p3 = &c; // 错误!子类指针不能指向父类对象,因为缺东西
derive* p4 = (derive*)&c; // 但是可以将父类对象强转为子类对象,不推荐这么做
p4->funa(); // 动态绑定,即使强转也不影响寻找父类的虚函数funa()
return 0;
}