多态:具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。(同一个接口根据调用对象不同产生不同的行为)。两个条件:
1. 必须通过基类的指针或者引用调用虚函数。
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
#include<iostream>
using namespace std;
class A
{
public:
virtual void Eat()//不加虚调用父类的加了虚再调动子类的
{
cout <<"A::Eat()" << endl;
}
virtual void Foot()
{
cout << "A::Foot()" << endl;
}
virtual void Sleep()
{
cout << "A::Sleep()" << endl;
}
};
class Person :public A
{
public:
void Eat()
{
cout << "Person::Eat()" << endl;
}
void Foot()
{
cout << "Person::Foot()" << endl;
}
void Sleep()
{
cout << "Person::Sleep()" << endl;
}
};
class Bird :public A
{
public:
void Eat()
{
cout << "Bird::Eat()" << endl;
}
void Foot()
{
cout << "Bird::Foot()" << endl;
}
void Sleep()
{
cout << "Bird::Sleep()" << endl;
}
};
class Dog :public A
{
public:
void Eat()
{
cout << "Dog::Eat()" << endl;
}
void Foot()
{
cout << "Dog::Foot()" << endl;
}
void Sleep()
{
cout << "Dog::Sleep()" << endl;
}
};
//class Car
//{
//private:
// Wheel two[2];
// Door four[4];
//};
void Active(A* pa)
{
pa->Eat();
pa->Foot();
pa->Sleep();
}
void test01()
{
Person p;
p.Eat();
Bird b;
b.Eat();
Dog d;
d.Eat();
}
void test02()
{
Person p;
Bird b;
Dog d;
Active(&p);
}
void main()
{
test02();
system("pause");
}
切片原则:父类指针或引用指向子类对象,假如没有构成重写,只要没构成重写就调用父类。因为父类指针或者引用只能调用对象在中的父类部分,调不动子类部分。拿子类对象赋值给父类对象也适用切片原则,只能调动父类的成员。
#include<iostream>
using namespace std;
class Base
{
public:
void fun()
{
cout << "Base::fun()" << endl;
}
private:
int m_a;
int m_b;
};
class D :public Base
{
public:
//既然改写了就说明父类的属性不合适子类,所以隐藏了
void fun()
{
cout << "D::fun()" << endl;
}
void show()
{
cout << "D::show()" << endl;
}
void fun(int a)
{
cout << "D::fun(int)" << endl;
}
private:
int m_c;
};
//这里就是按切片原理进行理解
void test01()
{
D d;
Base* pb = &d;
pb->fun();//父子类都有,调父类
//pb->show();//调不动子类特有的
//pb->fun(1);//调不动子类特有的
}
void main()
{
test01();
system("pause");
}
虚函数重写要求:
1.父子类的对应成员函数都是虚函数,子类可以不写virtual。
2.三同:返回值、函数名和参数列表必须相同。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void fun()
{
cout << " this is Base::fun()" << endl;
}
};
class D :public Base
{
public:
//重写 返回值 函数名 参数相同 三同
void fun(int a)
{
cout << "this is D::fun()" << endl;
}
};
void test01()
{
//父类指针或者引用指向子类对象
D d;
Base* pb = new D;
Base& refb = d;
pb->fun();//非虚调父类,虚调子类
refb.fun();//非虚调父类,虚调子类
}
void test02()
{
Base b;
D d;
b = d;
b.fun();//父类对象指向子类对象调父类方法,与虚不虚无关
}
void main()
{
test01();
system("pause");
}
通过计算带虚函数的类的大小,可以证明虚表指针的存在。32位系统下指针大小为4字节,64为系统下指针大小为8字节。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void fun()
{
cout << " this is Base::fun()" << endl;
}
virtual void fun1()
{
cout << "this is Base::fun1()" << endl;
}
virtual void fun2()
{
cout << "this is Base::fun2()" << endl;
}
private:
//_vfptr//虚表指针是每个对象的第一个成员,系统为对象添加的隐藏成员
int m_a;
};
void test01()
{
Base b;
cout << sizeof(Base) << endl;
}
void main()
{
test01();
system("pause");
}
子类的虚函数可以不写virtual,但是这样不规范,父类的虚函数在整个继承体系中,只要重写了它一直是虚函数。
#include<iostream>
using namespace std;
class Base
{
public: //子类的虚函数可以不写virtual,但是这样不规范
virtual void fun()//父类的虚函数在整个继承体系中,只要重写了它一直是虚函数
{
cout << " this is Base::fun()" << endl;
}
virtual void fun1()
{
cout << "this is Base::fun1()" << endl;
}
private:
//_vfptr//虚表指针是每个对象的第一个成员,系统为对象添加的隐藏成员
int m_a;
};
class D :public Base
{
virtual void fun()
{
cout << " this is D::fun()" << endl;
}
};
class D1 :public Base
{
virtual void fun()
{
cout << " this is D1::fun()" << endl;
}
};
void test01()
{
Base* pb = new D;
pb->fun();
Base* pb1 = new D1;//不同子类的重写
pb1->fun();
pb->fun1();//没重写,调父类
}
void main()
{
test01();
system("pause");
}
#include<iostream>
using namespace std;
class Base
{
public: //子类的虚函数可以不写virtual,但是这样不规范
virtual void fun()//父类的虚函数在整个继承体系中,只要子类重写了它一直是虚函数
{
cout << "this is Base::fun()" << endl;
}
virtual void fun1()
{
cout << "this is Base::fun1()" << endl;
}
private:
//_vfptr//虚表指针是每个对象的第一个成员,系统为对象添加的隐藏成员
int m_a;
};
class D :public Base
{
public:
void fun()
{
cout << "this is D::fun()" << endl;
}
void fun(int a)
{
cout << "D::fun(int)" << endl;
}
};
class C :public D
{
public:
void fun()
{
cout << "this is C::fun()" << endl;
}
void fun(int a)
{
cout << "C::fun(int)" << endl;
}
};
void test01()
{
D* pd = new C;//说明虽然没有写出virtual 但是D类中的fun显然是虚函数
pd->fun();
pd->fun(1);//没有构成虚函数重写,所以调用的是父类的
}
void main()
{
test01();
system("pause");
}
多态的两个特例:
1.协变:父类和子类的对应成员函数以其类型的引用或指针作为返回值,也可以构成虚函数重写从而实现多态。
2.虚析构函数的重写,析构函数设置为虚函数是为了防止子类对象在堆区开辟空间时,父类指针指向new出来的子类对象,delete父类指针时只能调用父类的析构函数而无法调用子类的析构函数,从而导致子类开辟在堆区的空间无法释放。
父类的构造函数没有写成虚函数,调用父类的析构函数而没有调动子类的析构函数。只对对象中的父类成员进行了释放和销毁,而没有对子类部分的数据进行释放和销毁。所以要求父类的析构函数为虚函数。
3.编译后析构函数的名称统一处理成destructor,所以才能构成重写。
#include<iostream>
using namespace std;
class Base
{
public:
Base()//父类的构造函数不能是虚函数
{
cout << "Base::Base()" << endl;
}
virtual ~Base()//编译后析构函数的名称统一处理成destructor
{
cout << "Base::~Base()" << endl;
}
public: //子类的虚函数可以不写virtual,但是这样不规范
virtual void fun()//父类的虚函数在整个继承体系中,只要子类重写了它一直是虚函数
{
cout << "this is Base::fun()" << endl;
}
private:
//_vfptr//虚表指针是每个对象的第一个成员,系统为对象添加的隐藏成员
int m_a;
};
class D :public Base
{
public:
D()
{
cout << "D::D()" << endl;
}
void fun()
{
cout << "this is D::fun()" << endl;
}
~D()
{
cout << "D::~D()" << endl;
}
};
void test01()
{
//D d;//正常构造析构
D* pd = new D;//正常调用父子类的构造
delete pd;//正常调动父子类析构
}
void test02()
{
Base* pb = new D;
delete pb;//父类的构造函数没有写成虚函数,调用父类的析构函数而没有调动子类的析构函数
//只对对象中的父类成员进行了释放和销毁,而没有对子类部分的数据进行释放和销毁
//所以要求父类的析构函数为虚函数
}
void main()
{
test02();
system("pause");
}
多态机制在父类的构造函数中不起作用 :即在父类的构造函数用this指针调重写的父类接口,执行的还是父类的虚函数。
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base::Base()" << endl;
this->fun();//多态机制在父类构造函数中不起作用,这里调父类的fun()
//这里对象都没有构造完成,不可能达到多态(不可能调动子类方法)
}
void list()
{
this->fun();//调动子类的fun()
}
virtual ~Base()
{
cout << "Base::~Base()" << endl;
}
public:
virtual void fun()
{
cout << "this is Base::fun()" << endl;
}
private:
int m_a;
};
class D :public Base
{
public:
D()
{
cout << "D::D()" << endl;
}
void fun()
{
cout << "this is D::fun()" << endl;
}
~D()
{
cout << "D::~D()" << endl;
}
};
void test01()
{
D d;
}
void test02()
{
D d;
d.list();
}
void main()
{
test02();
system("pause");
}
final关键字:写在需要构成重写的父类虚函数的后面,表示这个虚函数不能构成重写,假如这个虚函数在子类中构成重写,则会报错。
#include<iostream>
using namespace std;
class Car
{
public:
virtual void Drive() final {}//final 修饰虚函数终结了这个虚函数的后续继承 防止重写
};
class Benz :public Car
{
public:
virtual void Drive() { cout << "Benz-舒适" << endl; }
};
void test01()
{
Car* pc = new Benz;
pc->Drive();
}
void main()
{
test01();
system("pause");
}
override关键字:写在子类虚函数的后面,表示这个虚函数必须构成重写。假如这个虚函数没有构成重写则会报错。
#include<iostream>
using namespace std;
class Car {
public:
virtual void Drive() {}
};
class Benz :public Car {
public:
virtual void Drive() override { cout << "Benz-舒适" << endl; }//与父类对比看子类没有重写这个虚函数就会报错
virtual void Drive(int a) override { cout << "Benz-舒适" << endl; }//子类没有重写这个虚函数就会报错
virtual void Drive(int a, int b) { cout << "Benz-舒适" << endl; }
};
void test01()
{
Car* pc = new Benz;
pc->Drive();
}
void main()
{
test01();
system("pause");
}
观察底层的虚表可以发现子类的虚函数地址覆盖了父类的虚函数地址。
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base::Base()" << endl;
}
virtual void fun()
{
cout << "Base::fun()" << endl;
}
void fun(int a) {
cout << "Base::fun(int)" << endl;
}//重载
virtual void list()
{
cout << "Base::list()" << endl;
}
void print()
{
cout << "Base::print()" <<endl;
}
private:
int m_base = 1;
};
class D :public Base
{
public:
D()
{
cout << "D::D()" << endl;
}
void fun(int a){}//同名函数隐藏
virtual void fun()//重写(覆盖)
{
cout << "D::fun()" << endl;
}
};
void test01()
{
D d;
Base* pb = &d;
pb->fun(1);
}
void main()
{
test01();
system("pause");
}
父类必须写成析构的例子,假如不构成重写,会造成内存泄漏。
#include<iostream>
#include<vld.h>
using namespace std;
//父类不使用虚析构时不是说delete只释放了子类中的父类空间(没有释放子类对象空间)而是子类成员在堆上申请的空间
//有可能没有机会释放
class Base
{
public:
Base()
{
cout << "Base::Base()" << endl;
}
virtual void fun(int a) final//这个函数不重写不行 不然编译报错
{
cout << "Base::fun(int)" << endl;
}
virtual void fun()
{
cout << "Base::fun()" << endl;
}
virtual ~Base()
{
cout << "Base::~Base()" << endl;
}
};
class D :public Base
{
public:
D()
{
m_data = new char[10];
cout << "D::D()" << endl;
}
void fun() override//这个函数不构成重写不行,不然编译报错
{
cout << "D::fun()" << endl;
}
~D()
{
delete[] m_data;
cout << "D::D()" << endl;
}
private:
char* m_data;
};
void test01()
{
Base* pb = new D;
delete pb;
}
void main()
{
test01();
system("pause");
}
虚指针例题:
#include<iostream>
using namespace std;
// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
virtual void Func2()
{
cout << "Func2()" << endl;
}
private:
int _b = 1;
};
void main()
{
cout << sizeof(Base) << endl;
Base b;
system("pause");
}
一个类对应一张虚函数表,同一个类下的不同对象共用一张虚函数表。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 1;
};
class Derive : public Base
{
public:
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
virtual void func3()
{
cout << "Derive::Func3()" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b1;
Base b2;
Derive d1;
Derive d2;
d1.Func1();
return 0;
}
纯虚函数和抽象类:
1.含有纯虚函数的类叫做抽象类。
2.抽象类不能实例化对象。
3.子类继纯虚函数但不重写(即使只有一个没有重写)就还是抽象类 不能实例化对象。
编译时多态:函数地址早绑定,在编译时就能确定函数的调用关系,编译时底层改写了函数名。
运行时多态:函数地址晚绑定,运行时才能确定函数的调用关系,编译时只能确定是父类的指针在调用这个接口。
#include<iostream>
using namespace std;
class A
{
public:
virtual void Eat()
{
cout << "A::Eat()" << endl;
}
virtual void Sleep()
{
cout << "A::Sleep()" << endl;
}
virtual void Foot()
{
cout << "A::Foot()" << endl;
}
};
class Bird:public A
{
public:
virtual void Eat()
{
cout << "Bird::Eat()" << endl;
}
virtual void Sleep()
{
cout << "Bird::Sleep()" << endl;
}
virtual void Foot()
{
cout << "Bird::Foot()" << endl;
}
};
class Dog:public A
{
public:
virtual void Eat()
{
cout << "Dog::Eat()" << endl;
}
virtual void Sleep()
{
cout << "Dog::Sleep()" << endl;
}
virtual void Foot()
{
cout << "Dog::Foot()" << endl;
}
};
//运行时多态
void Active(A* pa)//传谁就调谁,在实现这里时有可能还不知道具体要调谁即main函数啥样
{
pa->Eat();
pa->Foot();
pa->Sleep();
}
void main()
{
Dog dog;
Bird b;
Active(&b);
}
重载、覆盖和隐藏的对比:重载应该是函数名相同参数列表不同
多态底层原理:
通过玩指针来得到虚函数表的各个函数指针:
先取b的地址,强转成一个int*的指针。再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针(虚表地址)。
虚函数表是一个函数指针数组,所以虚表地址可以看做首元素(第一个函数指针)的地址。
*(int*)(*(int*)&b)强转为int*再解引用得到前四个字节的值,再强转成pFun(函数指针)类型就得到了第一个虚函数指针。函数名和函数指针作用相同。
按照int*大小进行偏移再解引用就可以得到数组中其他元素的值,在进行强转得到其他虚函数的指针,按照函数名调用函数。
#include<iostream>
using namespace std;
typedef void(*pFun)();
class Base
{
public:
virtual void f()
{
cout << "Base::f()" << endl;
}
virtual void g()
{
cout << "Base::g()" << endl;
}
virtual void h()
{
cout << "Base::h()" << endl;
}
private:
int m_a=0;
int m_b=1;
};
void main()
{
Base b;
//cout << "对象的地址:" << &b << endl;
//cout << "虚表的地址:" << *(int*)&b << endl;//(int*)&b截取前四个字节是所保存的虚指针的地址,解引用获得指向 这里强转再解引用是整形
printf("虚表的地址:0x%p\n", *(int*)&b);
printf("对象的地址:0x%p\n", &b);
/*pFun pfun;
pfun =(void(*)()) (*(int*)&b);
pfun();*/
pFun pfun;
pfun = (pFun)(*((int*)(*(int*)&b)+0));
pfun();
pfun = (pFun)(*((int*)(*(int*)&b)+1));
pfun();
pfun = (pFun)(*((int*)(*(int*)&b) +2));
pfun();
动态绑定和静态绑定:
1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。
单继承和多继承时不同情况下的虚表结构:
单继承无虚函数覆盖:子类拷贝一份父类虚表,子类独有的虚函数指针按声明顺序追加到拷贝的表后面。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f()
{
cout << "Base::f()" << endl;
}
virtual void g()
{
cout << "Base::g()" << endl;
}
virtual void h()
{
cout << "Base::h()" << endl;
}
};
class D:public Base
{
public:
virtual void f1()
{
cout << "D::f1()" << endl;
}
virtual void g1()
{
cout << "D::g1()" << endl;
}
virtual void h1()
{
cout << "D::h1()" << endl;
}
};
void main()
{
D d;
}
单继承有虚函数覆盖:子类拷贝一份父类续表,子类重写父类的虚函数的指针会在对应位置进行覆盖,子类特有的虚函数按照声明顺序追加在这张虚函数表后面。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f()
{
cout << "Base::f()" << endl;
}
virtual void g()
{
cout << "Base::g()" << endl;
}
virtual void h()
{
cout << "Base::h()" << endl;
}
};
class D :public Base
{
public:
virtual void f()
{
cout << "D::f()" << endl;
}
virtual void g1()
{
cout << "D::g1()" << endl;
}
virtual void h1()
{
cout << "D::h1()" << endl;
}
};
void main()
{
D d;
}
多继承无虚函数覆盖:子类从每一个父类继承一张虚函数表,子类特有的虚函数的指针按声明顺序只追加在第一张虚表后面。
#include<iostream>
using namespace std;
class Base1
{
public:
virtual void f()
{
cout << "Base1::f()" << endl;
}
virtual void g()
{
cout << "Base1::g()" << endl;
}
virtual void h()
{
cout << "Base1::h()" << endl;
}
};
class Base2
{
public:
virtual void f()
{
cout << "Base2::f()" << endl;
}
virtual void g()
{
cout << "Base2::g()" << endl;
}
virtual void h()
{
cout << "Base2::h()" << endl;
}
};
class Base3
{
public:
virtual void f()
{
cout << "Base3::f()" << endl;
}
virtual void g()
{
cout << "Base3::g()" << endl;
}
virtual void h()
{
cout << "Base3::h()" << endl;
}
};
class D :public Base1,public Base2, public Base3
{
public:
virtual void f1()
{
cout << "D::f1()" << endl;
}
virtual void g1()
{
cout << "D::g1()" << endl;
}
virtual void h1()
{
cout << "D::h1()" << endl;
}
};
void main()
{
D d;
}
多继承有虚函数覆盖覆盖:子类从每一个父类继承一张虚函数表,子类重写的父类虚函数的函数指针会在对应父类的虚函数表中的对应位置进行覆盖,子类特有的虚函数的指针按声明顺序只追加在第一张虚表后面。
#include<iostream>
using namespace std;
class Base1
{
public:
virtual void f()
{
cout << "Base1::f()" << endl;
}
virtual void g()
{
cout << "Base1::g()" << endl;
}
virtual void h()
{
cout << "Base1::h()" << endl;
}
};
class Base2
{
public:
virtual void f()
{
cout << "Base2::f()" << endl;
}
virtual void g()
{
cout << "Base2::g()" << endl;
}
virtual void h()
{
cout << "Base2::h()" << endl;
}
};
class Base3
{
public:
virtual void f()
{
cout << "Base3::f()" << endl;
}
virtual void g()
{
cout << "Base3::g()" << endl;
}
virtual void h()
{
cout << "Base3::h()" << endl;
}
};
class D :public Base1,public Base2, public Base3
{
public:
virtual void f()
{
cout << "D::f1()" << endl;
}
virtual void g1()
{
cout << "D::g1()" << endl;
}
virtual void h1()
{
cout << "D::h1()" << endl;
}
};
void main()
{
D d;
}
另一种虚函数指针调用方式:
#include<iostream>
using namespace std;
class Base
{
public:
virtual void f()
{
cout << "Base::f()" << endl;
}
virtual void g()
{
cout << "Base::g()" << endl;
}
virtual void h()
{
cout << "Base::h()" << endl;
}
private:
int m_a=0;
int m_b=1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
f();
}
cout << endl;
}
int main()
{
Base b;
// 思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的指针数组,
//这个数组最后面放了一个nullptr
// 1.先取b的地址,强转成一个int*的指针
// 2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
// 3.再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。
// 4.虚表指针传递给PrintVTable进行打印虚表
// 5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面
VFPTR * vTableb = (VFPTR*)(*(int*)&b);
PrintVTable(vTableb);
//VFPTR* vTabled = (VFPTR*)(*(int*)&d);
//PrintVTable(vTabled);
return 0;
}
有问题的初始化方式,会导致虚表指针为空:
#include<iostream>
using namespace std;
class Base
{
public:
void f()
{
cout << "Base1::f()" << endl;
}
virtual void g()
{
cout << "Base1::g()" << endl;
}
virtual void h()
{
cout << "Base1::h()" << endl;
}
private:
int m_a;
int m_b;
};
class D:public Base
{
virtual void f()
{
cout << "D::f()" << endl;
}
virtual void g()
{
cout << "D::g()" << endl;
}
virtual void h()
{
cout << "D::h()" << endl;
}
};
void test01()
{
D d;
memset(&d, 0, sizeof(d));//不能这样初始化,虚表指针干空了
Base b;
Base* pb = &d;
//pb->f();
pb->g();
}
void main()
{
test01();
system("pause");
}
构造顺序例题:顺序为A,B,C,D,两个类同时虚拟继承了一个类,这个类只被构造一次
#include<iostream>
//两个类同时虚拟继承了一个类,这个类只被构造一次
using namespace std;
class A {
public:
A(const char* s) { cout << s << endl; }
~A() {}
};
class B :virtual public A
{
public:
B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C :virtual public A
{
public:
C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class D :public B, public C
{
public:
D(const char* s1, const char* s2, const char* s3, const char* s4) :B(s1, s2), C(s1, s3), A(s1)
{
cout << s4 << endl;
}
};
int main() {
D* p = new D("class A", "class B", "class C", "class D");
delete p;
return 0;
}
构造顺序:ABACD
#include<iostream>
using namespace std;
class A {
public:
A() { cout << "1111111111" << endl; }
~A() {}
};
class B : public A
{
public:
B() { cout << "2222222222" << endl; }
};
class C : public A
{
public:
C() { cout << "3333333333" << endl; }
};
class D :public B, public C
{
public:
D()
{
cout << "4444444444" << endl;
}
};
int main() {
D* p = new D;
delete p;
return 0;
}
构造顺序:BACACD
#include<iostream>
using namespace std;
class A {
public:
A() { cout << "1111111111" << endl; }
~A() {}
};
class B
{
public:
B() { cout << "2222222222" << endl; }
};
class C
{
public:
C() { cout << "3333333333" << endl; }
};
class D :public A, virtual public B, public C
{
public:
D()
{
cout << "4444444444" << endl;
}
private:
A a;
B b;
C c;
};
int main() {
D* p = new D;
delete p;
return 0;
}
例题:下面代码段输出是什么?
答案是输出B->1,这里p->test()会调用父类的test(),父类的中的this->func()满足多态原理,this指针指向B类对象,调动B类的func(),而子类重写虚函数时会继承接口而重写实现,val采用父类func的缺省参数1。
#include<iostream>
using namespace std;
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
B* p = new B;
p->test();
return 0;
}
多继承中指针偏移问题?下面说法正确的是( )
A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3
答案:C 有关于指针偏移的问题 指针偏向于先继承的那个父类。
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main(){
Derive d;
Base1* p1 = &d;
Base2* p2 = &d;
Derive* p3 = &d;
return 0;
}
1. 什么是多态?答:参考本博客。
2. 什么是重载、重写(覆盖)、重定义(隐藏)?答:参考本博客。
3. 多态的实现原理?答:参考本博客。
4. inline函数可以是虚函数吗?答:不能,因为inline函数没有地址,无法把地址放到虚函数表中。
5. 静态成员可以是虚函数吗?答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式。无法访问虚函数表,所以静态成员函数无法放进虚函数表。
6. 构造函数可以是虚函数吗?答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?答:可以,并且最好把基类的析构函数定义成虚函数。参考本博客。
8. 对象访问普通函数快还是虚函数更快?答:首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。
9. 虚函数表是在什么阶段生成的,存在哪的?答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。
10. C++菱形继承的问题?虚继承的原理?答:参考上一篇博客。注意这里不要把虚函数表和虚基表搞混了。
11. 什么是抽象类?抽象类的作用?答:参考本博客。抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。