virtual

C++中的virtual关键字主要有这样几种使用场景:第一,修饰父类中的函数 ;第二,修饰继承性。注意:友元函数、构造函数、static静态函数不能用virtual关键字修饰。普通成员函数和析构函数可以用virtual关键字修饰。

virtual具有继承性:父类中定义为virtual的函数在子类中重写的函数也自动成为虚函数。

一定要注意: 只有子类的虚函数和父类的虚函数定义完全一样才被认为是虚函数,比如父类后面加了const,如果子类不加的话就是隐藏了,不是覆盖.

修饰父类中的函数

修饰父类中的函数主要分为三种:普通函数、析构函数和纯虚函数。

修饰父类中的普通函数

被修饰的函数称为虚函数, 是C++中多态的一种实现(多说一句,多态分编译时多态-通过重载实现和运行时多态-通过虚函数实现)。 也就是说用父类的指针或者引用指向其派生类的对象,当使用指针或引用调用函数的时候会根据具体的对象类型调用对应对象的函数(需要两个条件:父类的函数用virtual修饰和子类要重写父类的函数)。下面用一个例子来说明:

#include <iostream>

class father {
public:
	void func1() {std::cout << "this is father func1" << std::endl;}
	virtual void func2() {std::cout << "this is father func2" << std::endl;
}

class son:public father {
public:
	void func1() {std::cout << "this is son func1" << std::endl;}
	void func2() {std::cout << "this is son func2" << std::endl;
}

int main() {
	father *f1 = new son();
	f1.func1();
	f1.func2();
	return 0;
}

output:

this is father func1
this is son func2

通过上面的例子可以看出,使用virtual修饰的函数会根据实际对象的类型来调用,没有使用virtual修饰的根据指针的类型来调用。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。

修饰析构函数

修饰析构函数与上面讲到的使用方法和原理相同,虚析构函数在销毁时会调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄露的问题或者程序直接崩溃。下面也用一个例子说明:

class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Fatherpublic GrandFather{
public:
	Father() {std::cout << "construct father" << std::endl;}
	~Father() {std::cout << "destruct father" << std::endl;}
};

class Sonpublic Father{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}

output:

construct grandfather
construct father
construct son
destruct father
destruct grandfather

我们发现没有调用son的析构函数,当将Father或者GrandFather其中一个的析构函数修改为virtual后输出就变为了

construct grandfather
construct father
construct son
destruct son
destruct father
destruct grandfather

纯虚函数

纯虚函数的定义是在虚函数的后面加一个=0。定义了纯虚函数的类是一个抽象类。

virtual void func() = 0;

纯虚函数需要注意这几点:
1.定义了纯虚函数的类不能够实例化,也就是不能够创建对象
2.继承了含有纯虚函数的父类的子类如果没有实现纯虚函数也不能够实例化

修饰继承性

假如有这种场景,一个类继承两个或者更多的父类,但是这些父类里又有一些有共同的父类,会出现什么情况呢?

class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Father1public GrandFather{
public:
	Father1() {std::cout << "construct father1" << std::endl;}
	~Father1() {std::cout << "destruct father1" << std::endl;}
};

class Father2public GrandFather{
public:
	Father2() {std::cout << "construct father2" << std::endl;}
	~Father2() {std::cout << "destruct father2" << std::endl;}
};

class Sonpublic Father1, Father2{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}

output:

construct grandfather
construct father1
construct grandfather
construct father2
construct son
destruct son
destruct father2
destruct grandfather
destruct father1
destruct grandfather

通过这个例子我们看到创建一个son会创建两个grandfather,不符合我们的预期啊,而且还可能会导致程序挂掉。这里就请virtual出场了,当把father1和father2继承grandfather修改为virtual继承(也就是在public前面加一个virtual)的时候输出会变成这样:
output:

construct grandfather
construct father1
construct father2
construct son
destruct son
destruct father2
destruct father1
destruct grandfather

这比较符合我们的预期。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐