【c++】虚函数,纯虚函数,抽象类

虚函数,纯虚函数,抽象类

  • 虚函数
  • 纯虚函数
  • 抽象类
    • 为什么抽象类不能创建对象?
    • 注意点:
  • 接口
  • 类如何设计
  • 虚析构
    • 总结:
  • 构造函数为什么不能是虚函数
  • 有虚函数的对象创建过程

虚函数

虚函数:给成员函数前面加上virtual关键字。

1.派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,同返回类型。否则被认为是重载,而不是重写。如基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外(协变)。

2.只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。友元函数和全局函数不能作为虚函数。

3.静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数。

4.内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。

5.构造函数(拷贝构造,移动构造)不能作为虚函数。因为构造函数的另一个作用是设置虚表指针。

6.析构函数可定义为虚函数,在基类中及其派生类中都获取系统资源时时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。

7.实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性。

8.虚函数执行速度要稍慢一些,为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。

9.如果虚函数定义放在类外,virtual只能加在函数声明前面,不能再加在函数定义前面。

10.如果不使用继承和多态,那么就把函数中的virtual关键字去掉,加上的话virtual也没必要,还会增大程序的开销。

纯虚函数

纯虚函数:没有具体实现的虚成员函数

定义纯虚函数的一般格式为:

virtual  返回类型  函数名(参数表) = 0;

“=0"表明程序员将不定义该函数,函数声明是为派生类保留一个位置。”=0"本质上是将指向函数体的指针定为NULL(不同编译器的处理不同)。
告诉编译器我们不实现这个函数,由派生类来实现。

抽象类

含义纯虚函数的类是抽象类

1.抽象类不能实例化对象
2.派生类继承了抽象类,则必须把抽象类中的纯虚函数实现了,否则也不能创建对象
3.在程序设计时,抽象类一般都做为基类来使用
当基类继承抽象类时,必须把抽象类中的所有纯虚函数进行重写,如果有一个没有重写,那么基类就是抽象类,全部实现之后,就称为(实现类)
4.抽象类不能做参数类型函数返回值类型或显示类型转换,抽象类型可以定义指针和引用(可以指向它不同的派生对象,以实现运行时多态)

应用类型:不提供派生也不提供继承
节点类型:提供继承和多态的继承(有虚函数),但没有纯虚函数
抽象类型:抽象类,只能用作基类
接口类:没有属性,所有函数都是纯抽象函数
实现类:继承了接口或者抽象类,实现了纯虚函数

为什么抽象类不能创建对象?

纯虚函数没实现时,对应的该函数指向nullpter(不同的环境实现不同),当创建对象时,进行查虚表,当它的函数为nullpter时,则会报错(纯虚函数会在虚表中保留一个位置,让派生类实现虚表时,把它对该函数的具体实现的地址填充进去它)
visaul stdio的实现:它则把这些函数指向一个函数(默认的缺省的纯虚函数,调用时会抛出异常)

注意点:

class Base
{
public:
	virtual void fun() = 0;
	virtual ~Base(){}
};
class Child :public Base
{
public:
	virtual void fun()const {}
	virtual ~Child(){}
};

Child 可以创建对象吗?

不能,因为这是同名隐藏(两个函数的参数列表不同),不属于同名覆盖。去掉const则就可以了

接口

类中全部都是成员方法,没有属性,而且成员方法全部都是纯虚函数

类如何设计

  • 有时希望派生类只继承成员函数的接口(声明),纯虚函数。
  • 有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现,虚函数。
  • 有时则希望同时继承接口和实现,并且不允许派生类改写任何东西,非虚函数。

申明纯虚函数的目的,使派生类继承函数的接口和强制性实现

虚析构

如果抽象类里面有虚函数,建议把析构函数定义成虚函数,

防止出现内存泄漏

using namespace std;
class Base
{
public:
	 virtual ~Base() { cout << "delete base" << endl; }
};
class Child :public Base
{
public:
	
	 ~Child(){ cout << "delete Child" << endl; }
};
int main()
{
	Base * c = new Child();
	delete c;
	return 0;
}

运行结果
【c++】虚函数,纯虚函数,抽象类_第1张图片
如果没虚析构的话

using namespace std;
class Base
{
public:
	  ~Base() { cout << "delete base" << endl; }
};
class Child :public Base
{
public:
	
	 ~Child(){ cout << "delete Child" << endl; }
};
int main()
{
	Base * c = new Child();
	delete c;
	return 0;
}

【c++】虚函数,纯虚函数,抽象类_第2张图片

总结:

(1)如果父类的析构函数不加virtual关键字
当父类的析构函数不声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,只调动父类的析构函数,而不调动子类的析构函数。
(2)如果父类的析构函数加virtual关键字
当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调动子类的析构函数,再调动父类的析构函数。

构造函数为什么不能是虚函数

因为构造函数的一个作用就是设置虚表指针,如果把构造函数设置成虚函数,那么构造函数将在虚表中存着,那么在调用构造函数的时候,虚表指针还没有设置,这样就无法找到虚表,就无法调用构造函数,而要设置虚表指针,就必须得调用构造函数,此时就已经产生矛盾了,所以构造函数不能定义为虚函数。

有虚函数的对象创建过程

1.确定this指针。(先确定this指针才可以对之后的进行构建,靠ecx传递过来)
2.如果有虚函数,则让虚表指针指向虚函数表(存放在.data)
3.构造其它成员属性

你可能感兴趣的:(C++,c++)