虚函数可以实现多态特性,因此在设计模式中很有用,配合继承的特性可以提供很好的解决方案
1.impure virtual
- 基类指定接口和缺省的实现方式,派生类重载基类中的虚函数,定义自己的实现方式,然后通过基类指针指向从而实现多态。
class Man
{
public:
virtual void sayHello()
{
std::cout << "Hello" << std::endl;
}
virtual void walk()
{
std::cout << "walk" << std::endl;
}
};
class Teacher:public Man
{
public:
void sayHello()
{
std::cout << "Hello,Teacher" << std::endl;
}
};
Man m;
m.sayHello();
Man *pm = new Teacher;
pm->sayHello();
- 虚函数可以不在子类里重载
2.pure virtual
- 定义了纯虚函数的类叫抽象基类,而抽象基类负责定义接口,后续的类可以覆盖并且必须重新定义实现该函数,抽象类不可以创建对象
class Airplane
{
public:
virtual void fly() = 0;
};
class Model1 :public Airplane
{
void fly() { std::cout << "Model1 fly" << std::endl; }
};
class Model2 :public Airplane
{
void fly() { std::cout << "Model2 fly" << std::endl; }
};
- 抽象类纯虚函数一般不会有实现部分,但有时也可以有,主要是为了子类同时可以继承接口和一部分缺省实现
class Airplane
{
public:
virtual void fly() = 0;
};
void Airplane::fly()
{
std::cout << "default fly" << std::endl;
}
class Model1 :public Airplane
{
void fly() { std::cout << "Model1 fly" << std::endl; }
};
class Model2 :public Airplane
{
void fly() { std::cout << "Model2 fly" << std::endl; }
};
class Model3 :public Airplane
{
void fly() { Airplane::fly(); }
};
3.虚基类
- 多继承时很容易产生命名冲突,因此,当一个基类被声明为虚基类后,即使它成为了多继承链路上的公共基类,最后的派生类中也只有它的一个备份。
class CBase { };
class CDerive1:virtual public CBase{ };
class CDerive2:virtual public CBase{ };
class CDerive12:public CDerive1,CDerive2{ };
//则在类CDerive12的对象中,仅有类CBase的一个对象数据
- 虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承),虚基类的构造函数先于非虚基类的构造函数执行。
/**//************************************************************************
* 混合继承:多基类继承与多重继承
************************************************************************/
#include <IOSTREAM.H>
//基类
class CBase
...{
protected:
int a;
public:
CBase(int na)
...{
a=na;
cout<<"CBase constructor! ";
}
~CBase()...{cout<<"CBase deconstructor! ";}
};
//派生类1(声明CBase为虚基类)
class CDerive1:virtual public CBase
...{
public:
CDerive1(int na):CBase(na)
...{
cout<<"CDerive1 constructor! ";
}
~CDerive1()...{cout<<"CDerive1 deconstructor! ";}
int GetA()...{return a;}
};
//派生类2(声明CBase为虚基类)
class CDerive2:virtual public CBase
...{
public:
CDerive2(int na):CBase(na)
...{
cout<<"CDerive2 constructor! ";
}
~CDerive2()...{cout<<"CDerive2 deconstructor! ";}
int GetA()...{return a;}
};
//子派生类
class CDerive12:public CDerive1,public CDerive2
...{
public:
CDerive12(int na1,int na2,int na3):CDerive1(na1),CDerive2(na2),CBase(na3)
...{
cout<<"CDerive12 constructor! ";
}
~CDerive12()...{cout<<"CDerive12 deconstructor! ";}
};
void main()
...{
CDerive12 obj(100,200,300);
//得到从CDerive1继承的值
cout<<" from CDerive1 : a = "<<obj.CDerive1::GetA();
//得到从CDerive2继承的值
cout<<" from CDerive2 : a = "<<obj.CDerive2::GetA()<<endl<<endl;
}
此代码出处
4.虚析构函数的妙用
对于基类指针指向派生类时,delete该指针应该相应的执行该子类的析构函数,所以需要将基类定义成virual
class Base{
public:
virtual ~Base()=default;
}
5.虚函数表
- 每个含有虚函数的类有一张虚函数表,表中每一项是一个虚函数的地址, 也就是说,虚函数表的每一项是一个虚函数的指针。
6.非虚函数接口的实现
….
7.总结
- 虚函数带来的多态效应在实际中非常有用,一般C++的项目设计派生时,尽量将函数加上virtual,这样子类可以更加灵活
- 纯虚函数关注的是接口继承和默认行为,可以防止被实例化
- -