多态
C++实现多态的前提条件,存在继承关系,子类重写父类虚函数,并且父类指针调用子类重写的虚函数;
静态多态(编译时多态):函数重载(运算符重载),编译时可以确定的多态。
动态多态:通过指针或引用调用子类重写的虚函数,在程序运行期间才能确定具体调用哪一 函数个虚函数。
函数隐藏:子类中函数名和父类中函数名相同,子类对应函数会屏蔽父类同名函数。
函数的覆盖发生在基类和子类中,并且两个函数完全相同(且为虚函数),否则为函数隐藏
C++多态的实现
#include<iostream>
using namespace std;
class father
{
public:
void work()
{
cout << "father work" << endl;
}
virtual void fun()
{
cout << "father fun" << endl;
}
virtual void story()
{
cout << "father story" << endl;
}
virtual void inter(int n)
{
cout << "father inter" << endl;
}
};
class son :public father
{
public:
void work()
{
cout << "son work" << endl;
}
virtual void fun()
{
cout << "son fun" << endl;
}
void story()
{
cout << "son story" << endl;
}
virtual void inter(int n)
{
cout << "son inter" << endl;
}
};
void happy(father* fath)
{
fath->fun();//基类指针可以指向子类对象
}
int main()
{
father* f = new son;
f->fun(); //输出son fun
f->work(); //输出father work
f->story(); //输出son story(子类中的virtual省略,依然发生重写)
son* S = new son;
father& M = *S; //引用
M.work(); //输出father work
M.inter(1); //输出son inter
son so;
happy(&so); //输出son fun
return 0;
}
虚表指针与虚函数
class A
{
public:
virtual void vfun1();//虚函数
virtual void vfun2();
void fun1();//普通函数
void fun2();
};
class B :public A //此时类B也有自己的虚表
{
};
- 每个包含虚函数的类都有用来存放指针的数组(虚表),其中每一个元素对应该类的一个虚函数
- 含虚函数类中的虚表,而非对象所有,一个类所有对象共用一个虚表,如果一个子类继承父类,父类若有虚函数,子类可调用父类的虚函数,子类也会有虚表。
- 虚函数指针的赋值发生在编译器的编译阶段,在编译阶段虚表就构造出来了。
- 为了让每个包含虚表的类对象都拥有一个虚表指针,编译器在类中添加了一个指针*_vptr用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个只针对值会自动被设置为指向该类的虚表
动态绑定
class A
{
public:
virtual void vfunc1() {}
virtual void vfunc2() {}
void func1() {}
void func2() {}
private:
int m_data1, m_data2;
};
class B:public A
{
public:
virtual void vfunc1() {}
void func1() {}
private:
int m_data3;
};
class C :public B
{
public:
virtual void vfunc2() {}
void func2() {}
private:
int m_data1, m_data4;
};
静态类型和动态类型
静态类型:对象/变量 声明时的类型,在编译时确定
动态类型:对象/变量实际指向的类型,程序运行时确定
静态绑定和动态绑定
静态绑定(编译时绑定):
绑定对象的静态类型-适用于非虚函数,成员变量,模板等
动态绑定(运行时绑定):
绑定对象的动态类型-仅适用于虚函数
class A { virtual void vfun() { ... } }; //动态绑定:虚函数 vfun
class B : public A { void vfun() override { ... } };
A* a = new B;
a->vfun(); // 调用B::vfun(),动态绑定
虚析构
基类使用虚析构函数能够通过基类指针删除派生类对象的时候可以调用完整析构链(避免内存泄漏)
析构链:(基类构造->派生类构造->派生类析构->基类析构)
class A {
public:
virtual ~A() { ... } // 虚析构函数
};
class B : public A {
public:
~B() override { ... } // 自动成为虚函数
};
A* a = new B;
delete a; // 正确调用B::~B()和A::~A()
纯虚函数与抽象类
纯虚函数:声明时赋值为零,无默认实现
抽象类:包含纯虚函数的类
*抽象类不可创建对象,若抽象类的子类中仍存在纯虚函数,那么该派生类仍为抽象类