目录
1.多态的定义及实现
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
补充:虚函数的重写有两个例外
1. 协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指 针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
2. 析构函数的重写(基类与派生类析构函数的名字不同
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字, 都与基类的析构函数构成重写,可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数称统一处理成destructor。
举例:
class AA
{
public:
AA(int a=5)
{
_a = new int[a];
}
virtual void person()
{
cout << "AAperson" << endl;
}
virtual ~AA()
{
delete[] _a;
cout << "delete AA" << endl;
}
private:
int* _a;
};
class BB:public AA
{
public:
BB(int b=9)
{
_b = new int[b];
}
virtual void person()
{
cout << "BBperson" << endl;
}
~BB()
{
delete[] _b;
cout << "delete BB" << endl;
}
private:
int* _b;
};
void show(AA* p)
{
p->person();
}
int main()
{
AA* A = new AA;
show(A);
delete A;
A = new BB;
show(A);
delete A;
return 0;
}
结果:
AAperson
delete AA
BBperson
delete BB
delete AA
2.C++11 override 和 fifinal
1. fifinal:修饰虚函数,表示该虚函数不能再被重写 ,修饰类,表示该类不能被继承。
2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car {
public:
virtual void Drive()
{}
};
class Benz :public Car {
public:
virtual void Drive() override
{
cout << "Benz-舒适" << endl;;
}
};
3. 抽象类
概念:虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
例如:
4.多态的原理
4.1虚函数表
先看一下A的大小:
class BB: public AA
{
public:
void person()
{
cout << "BBprosn" << endl;
}
private:
int _b;
};
int main()
{
AA A;
cout << sizeof(A) << endl;
return 0;
}
A对象里面有一个int型的数据和一个指针(64位平台下),内存对齐后为16个字节。
![](https://i-blog.csdnimg.cn/blog_migrate/5293457ddff55d15f6e271f118f94db8.png)
将AA类中 AAfun()继承下来了,person()完成了重写。
1.构成多态,指向谁调用的就是谁的虚函数,跟对象有关。
2.不构成多态,对象类型是什么,调用那个函数就跟类型有关
那为什么必须是父类的指针或者引用调用虚函数时发生多态,不能是对象呢?对象切片时,子类对象只会拷贝成员给父类对象,不会拷贝虚表指针。
只要是虚函数就会进虚表指针,那么B对象中的BBfun函数的地址应该也在虚表。可在内存窗口查看。
也可进一步验证,将虚函数的地址打印出来。(32位机器下)
4.2动态绑定与静态绑定
5.多继承关系的虚函数表
多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中
class Base1 {
public:
virtual void func1() { cout << "Base1::func1" << endl; }
virtual void func2() { cout << "Base1::func2" << endl; }
private:
int b1;
};
class Base2 {
public:
virtual void func1() { cout << "Base2::func1" << endl; }
virtual void func2() { cout << "Base2::func2" << endl; }
private:
int b2;
};
class Derive : public Base1, public Base2 {
public:
virtual void func1() { cout << "Derive::func1" << endl; }
virtual void func3() { cout << "Derive::func3" << endl; }
private:
int d1;
};
int main()
{
Derive d;
Base1* p1 = &d;
Base2* p2 = &d;
Derive* p3 = &d;
return 0;
}
6.继承和多态常见面试题
1.
class A {
public:
A(char* s)
{
cout << s << endl;
}
~A() {}
};
class B :virtual public A {
public:
B(char* s1, char* s2)
:A(s1)
{
cout << s2 << endl;
}
};
class C :virtual public A {
public:
C(char* s1, char* s2)
:A(s1)
{
cout << s2 << endl;
}
};
class D :public B, public C {
public:
D(char* s1, char* s2, char* s3, 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;
}
按声明的顺序(继承的顺序)构造。
2.多继承中指针偏移问题?下面说法正确的是( )
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; }
p1==p3!=p2
nline函数可以是虚函数吗?答:可以,不过编译器就忽略inline属性,这个函数就不再是
inline,因为虚函数要放到虚函数表中
静态成员可以是虚函数吗?答:不能,因为静态成员函数没有this指针,使用类型::成员函数
的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表
构造函数可以是虚函数吗?答:不能,因为对象中的虚函数表指针是在构造函数初始化列表
阶段才初始化的
虚函数表是在什么阶段生成的,存在哪的?答:虚函数表是在编译阶段就生成的,一般情况
下存在代码段(常量区)的
C++菱形继承的问题?虚继承的原理? 虚基表:存的是偏移量,解决数据冗余和二义性问题一
虚函数表:存的是虚函数地址,是为了实现多态