1.虚函数
- 代码示例
class Base
{
public:Base(){}
public:
virtual void print(){cout<<"Base";}
};
class Derived:public Base //base是基类(父类),Derived是派生类(子类)
{
public:Derived(){}
public:
void print(){cout<<"Derived";}
};
int main()
{
Base *point=new Derived();
point->print();
}
输出:Derived
- 原因:基类的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的。
- 和函数重载的区别
- 函数重载:
- 重载的几个函数必须在同一个类中;
- 重载的函数必须函数名相同,参数不同。
- 重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。
- 函数覆盖:
- 覆盖的函数必须在有继承关系的不同的类中
- 覆盖的几个函数必须函数名、参数、返回值都相同
- 覆盖的函数前必须加关键字Virtual
- 函数重载:
- 另外:决定基类指针或者派生类指针调用函数运行结果的不是他们指向的地址,而是他们的指针类型。
2.纯虚函数
纯虚函数的定义如下:
C++语言为我们提供了一种语法结构,通过它可以指明,一个虚拟函数只是提供了一个可被子类型改写的接口。但是,它本身并不能通过虚拟机制被调用。这就是纯虚拟函数(purevirtual function)。纯虚拟函数的声明如下所示:
class Query {
public:
// 声明纯虚拟函数
virtual ostream& print( ostream&=cout ) const = 0;
// ...
};
这里函数声明后面紧跟赋值0。
包含一个或多个纯虚拟函数的类被编译器识别为抽象基类。抽象基类不能被实例化,一般用于继承。抽象基类只能作为子对象出现在后续的派生类中
【抽象类:C++ 接口(抽象类)】
3.虚拟继承
虚继承:在继承定义中包含了virtual关键字的继承关系;
虚基类:在虚继承体系中的通过virtual继承而来的基类;
- 代码示例1
class A
{
void f1(){};
};
class B : public virtual A{
void f2(){};
};
在多继承下,虚继承就是为了解决菱形继承中,B,C都继承了A,D继承了B,C,那么D关于 A的引用只有一次,而不是 普通继承的 对于A引用了两次……
- 代码示例2
#include
using namespace std;
class Person{
public: Person(){ cout<<"Person构造"<<ENDL; }
~Person(){ cout<<"Person析构"<<ENDL; }
};
class Teacher : virtual public Person{
public: Teacher(){ cout<<"Teacher构造"<<ENDL; }
~Teacher(){ out<<"Teacher析构"<<ENDL; }
};
class Student : virtual public Person{
public: Student(){ cout<<"Student构造"<<ENDL; }
~Student(){ cout<<"Student析构"<<ENDL; }
};
class TS : public Teacher, public Student{
public: TS(){ cout<<"TS构造"<<ENDL; }
~TS(){ cout<<"TS析构"<<ENDL; }
};
int main(int argc,char* argv[])
{
TS ts;
return 0;
}
这段代码的输出结果为:
Person构造
Teacher构造
Student构造
TS构造
TS析构
Student析构
Teacher析构
Person析构
当Teacher类和Student类没有虚继承Person类的时候,也就是把virtual去掉时候终端输出的结果为:
Person构造
Teacher构造
Person构造
Student构造
TS构造
TS析构
Student析构
Person析构
Teacher析构
Person析构
Teacher类和Student类没有虚继承Person类的结果不是我们所期望的。原因是Teacher类和Student类都继承于Person类。导致了构造TS的时候实例化了两个Person类。同样的道理,析构的时候也是析构了两次Person类。这是非常危险的,也就引发出了virtual的第三种用法,虚析构。
虚析构
#include <iostream>
using namespace std;
class Person{
public: Person(){ cout<<"Person构造"<<endl; }
virtual ~Person(){ cout<<"Person析构"<<endl; }
};
class Teacher : public Person{
public: Teacher(){ cout<<"Teacher构造"<<endl; }
~Teacher(){ cout<<"Teacher析构"<<endl; }
};
int main(int argc,char* argv[])
{
Person *p= new Teacher;
delete p;
return 0;
}
输出:
Person构造
Teacher构造
Teacher析构
Person析构
但是如果把上面语句 virtual ~Person(){ cout<<“Person析构”<<endl; }中的virtual去掉则代码运行结果会缺少Teacher析构。
虚析构总结一下就是:
(1)如果父类的析构函数不加virtual关键字
当父类的析构函数不声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,只调动父类的析构函数,而不调动子类的析构函数。
(2)如果父类的析构函数加virtual关键字
当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调动子类的析构函数,再调动父类的析构函数。