C++——虚函数、虚析构函数、纯虚函数、抽象类

目录

一、虚函数

1、什么是虚函数?

2、虚函数的作用

 3、虚函数的参数

4、虚函数代码示例

二、虚析构函数

1、什么是虚析构函数?

2、虚析构函数的作用

 3、 构造函数

4、为什么构造函数不能是虚函数?

5、构造函数和虚析构函数的联系

6、为什么程序员不能调构造函数,但是可以调用析构函数?

7、虚析构函数产生多态

三、纯虚函数

1、定义:

2、纯虚函数的一般格式

 3、例题理解

四、抽象类

1、抽象类的定义

2、抽象类的主要作用 

一、虚函数

1、什么是虚函数?

  •   被 virtual 关键字修饰的成员函数称为虚函数。
  •  在类中定义了虚函数就会有一个虚函数表(vftable),对象模型中就含有一个指向虚表的指针(__vfptr)。在定义对象时构造函数设置虚表指针指向虚函数表。
  • 使用指针和引用调用虚函数,在编译只需要知道函数接口,运行时指向具体对象,才能关联具体对象的虚方法(通过虚函数指针查虚函数表得到具体对象中的虚方法)。

2、虚函数的作用

     实现多态性,即通过基类访问派生类的函数。

 3、虚函数的参数

  虚函数的参数值是默认静态绑定的,在子类中 重新定义虚函数的时候,并没有重新定义继承而来的参数值;除非是在实际调用过程中传递了想用的参数。 

4、虚函数代码示例

class Parent
{
public:
	virtual void fn(int a = 10)//如果虚函数有值,虚函数的参数值是默认静态绑定
	{
		cout << "parent fn a=" << a<<endl;
	}
};
class Child :public Parent
{
public:
	virtual void fn(int b = 100)
	{
		cout << "child fn b=" << b << endl;
	}
};
int main()
{
	Child cc;
	Parent* p = &cc;
	//p->fn(200);//chlid fn b=200;
	p->fn();//child fn b=10;产生了动态绑定,但是虚函数的参数是默认绑定
	cc.fn();//child fn b=100;
}

二、虚析构函数

1、什么是虚析构函数?

  •       派生类的析构函数会自动调用基类的析构函数。 只要基类的析构函数是虚函数,那么派生类的析构函数不论是否用virtual关键字声明,都自动成为虚析构函数
  •      一般来说,一个类如果定义了虚函数,则最好将析构函数也定义成虚函数。 析构函数可以是虚函数,但是构造函数不能是虚函数。(第4点有具体原因说明)
  • 注意:类中没有虚函数,就不要把析构函数定义为虚。
    在动态分配内存时所有C++的标准库函数都采用这种格式。

2、虚析构函数的作用

为了解决基类指针指向派生类对象,并用基类的指针删除派生类对象。  

 3、 构造函数

构造函数是类的一个特殊的成员函数 :

  • 1)  当一个对象的生命周期结束时,系统会自动调用析构函数注销该对象并进行善后工作,对象自身也可以调用析构函数;
  • 2)析构函数的善后工作是:释放对象在生命期内获得的资源(如动态分配的内存,内核资源);
  • 3)  析构函数也用来执行对象即将被撤销之前的任何操作。

4、为什么构造函数不能是虚函数?

       根据赋值兼容规则,可以用基类的指针指向派生类对象,如果使用基类型指针指向动态创建的派生类对象,由该基类指针撤销派生类对象,则必须将析构函数定义为虚函数,实现多态性,自动调用派生类析构函数,否则可能存在内存泄漏问题

总结:在实现运行时的多态,无论其他程序员怎样调用析构函数都必须保证不出错,所以必须把析构函数定义为虚函数。

5、构造函数和虚析构函数的联系

  • 如果构造函数可以定义为虚构造函数,使用指针调用虚构造函数,
  • 如果编译器采用静态联编,构造函数就不能为虚函数。
  • 如果采用动态联编,运行时指针指向具体对象,使用指针调用构造函数,相当于已经实例化的对象在调用构造函数,这是不容许的调用,对象的构造函数只执行一次。 

6、为什么程序员不能调构造函数,但是可以调用析构函数?

原因:

  •      虚函数调用只需要"部分的"信息,即只需要知道函数接口,而不需要对象的具体类型。但是构建一个对象,却必须知道具体的类型信息。
  •      如果你调用一个虚构造函数,编译器无法知道你想构建是继承树上的哪种类型,所以构造函数不能为虚。

7、虚析构函数产生多态

多态特例——虚析构函数产生多态;
类有指针作为数据成员,必须要写析构函数,如果当前类被继承了,则析构函数写成virtual
为了实现多态,将子类的空间被合理的释放,防止内存泄露 。

class A {
public:
	A()
	{
		cout << "A" << endl;
		m_i = new int;
	}
	virtual ~A()
	{
		cout << "~A" << endl;
		delete m_i;
	}
private:
	int* m_i;
};
class B :public A
{
public:
	B()
	{
		cout << "B" << endl;
		m_j = new int;
	}
	virtual ~B()
	{
		cout << "~B" << endl;
		delete[]m_j;
	}
private:
	int* m_j;

};
void test(A* pb)
{
	;
}
int main()
{
	A* pb = new B;//new B类对象,先调用基类构造函数,再调用子类构造函数,
	delete pb;//pb指向子类对象,但是pb是A类类型指针,所以只析构了A,没有调子类类型的析构函数,存在内存泄漏。

}

三、纯虚函数

1、定义:

       纯虚函数(pure virtual function)是指没有具体实现的虚成员函数。它用于这样的情况:设计一个类型时,会遇到无法定义类型中虚函数的具体实现,其实现依赖于不同的派生类。

2、纯虚函数的一般格式

                          virtual返回类型函数名(参数表)=0;
“=0"表明程序员将不定义该虚函数实现,没有函数体,只有函数的声明;函数的声明是为了在虚函数表中保留一个位置。在虚函数()的基础上=0;“=0“体质上是将指向函数体的指针定义为nullptr。

 3、例题理解

 题目:
设计一个形状类——有矩形、圆形、三角形;分别计算三种图形的周长和面积;

class Shape
{
public:
	virtual void Area() = 0;//纯虚函数就是在虚函数的基础上给个0
	virtual void Girth() = 0;//相当于把函数指针指向空
	

};
class Rectangle :public Shape
{
public:
	virtual void Area() { cout << "Rectangle area"<< m_length*m_width << endl; }
	virtual void Girth(){ cout << "Rectangle girth " << 2*(m_length+m_width)<<endl; }

private:
	int m_length;
	int m_width;
};

class Circle :public Shape
{
public:
	virtual void Area() { cout << "Circle area "<<3.14*m_ra*m_ra << endl; }
	virtual void Girth() { cout << "Circle girth"<< 2*3.14*m_ra<< endl; }

private:
	int m_ra;//圆的半径

};

class Triangle :public Shape
{
public:
	virtual void Area() { cout << "Triangle  area " << endl; }
	virtual void Girth() { cout << "Triangle girth "<<3*m_length << endl; }

private:
	int m_length;
	
};
void test(Shape* p)
{
	p->Area();
	p->Girth();
}

int main()
{
	Shape* pf[3];
	pf[0] = new Rectangle;
	pf[1] = new Circle;
	pf[2] = new Triangle;

	for (int i = 0;i < 3;i++)
	{
		pf[i]-> Area();
		pf[i]->Girth();
		delete pf[i];
		pf[i] = NULL;
	}
	/*Shape s;
	s.Area();
	s.Girth();*/
}

四、抽象类

1、抽象类的定义

  • 含有纯虚函数的类是抽象类。
  • 抽象类是一种特殊的类,它是为抽象的目而建立的,它处于继承层次结构的较上层。
  • 抽象类不能实例化对象,因为纯虚函数没有实现部分,所以含有纯虚函数类型不能实例化对象;
  • 注意:1)在定义纯虚函数时,不能定义虚函数的实现部分;

    2)在没有重新定义这种纯虚函数之前,是不能调用这种函数的。

2、抽象类的主要作用 

  • 将相关的类型组织在一个继承层次结构中,抽象类为派生类型提供一个公共的根,相关的派生类型。
  •  为了派生子类,作为当前类族最上面的基类出现,则在子类中必须要重写基类中的纯虚函数(具体类)。
  • 如果在子类中没有实现纯虚函数,则子类也是抽象类。

如有错误,敬请指正。

您的收藏与点赞都是对我最大的鼓励和支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sweep-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值