C++学习笔记 -- 虚析构函数与纯虚析构函数

简介: 开始学C++了,所以又重拾以前学习过的相关概念… 析构函数是当一个对象的生命周期结束时,会自动执行析构函数。 析构函数的定义: #ifndef __A_H__#define __A_H__class A{public: A(void); A(int a, int b); ...

开始学C++了,所以又重拾以前学习过的相关概念…

析构函数是当一个对象的生命周期结束时,会自动执行析构函数。

析构函数的定义:


#ifndef __A_H__
#define __A_H__

class A
{
public:
A(void);
A(int a, int b);
~A(void); //析构函数

private:
int a;
int b;
int c;
};

#endif

虚析构函数与纯虚析构函数的定义(假定类名为A):


#ifndef __A_H__
#define __A_H__

class A
{
public:
A(void);
A(int a, int b);
virtual ~A(void); //虚析构函数

private:
int a;
int b;
int c;
};

#endif


#ifndef __A_H__
#define __A_H__

class A
{
public:
A(void);
A(int a, int b);
virtual ~A(void) = 0; //纯虚析构函数

};

#endif

其中定义了纯虚函数后,类A就成为了“抽象类”,它是不能有实例对象的。否则会报错:

“A”: 不能实例化抽象类

由于下列成员:

“A::~A(void)”: 是抽象的

一个类维护一个虚函数相关的表--vtable(__vfptr指向它),函数声明前面包含关键字“virtual”的函数,就会创建一个指向该函数的指针(函数指针)被存入vtable中。

虚函数表的作用是用来实现多态,但同时也带来了执行效率和额外内存空间的增加。

 

再来看虚析构函数,它所存在的意义:基类的指针指向派生类对象,用基类的指针删除派生类对象。


#include <iostream>
using namespace std;

class A
{
public:
A()
{
cout <<"A..."<<endl;
}
~A()
{
cout <<"~A..."<<endl;
}
};

class B :public A
{
public :
B()
{
cout <<"B..."<<endl;
}
~B()
{
cout <<"~B..."<<endl;

}
};

int main()
{
A *a = new B();
delete a;

return 0 ;
}

输出:

A…

B…

~A…

派生类的析构函数未被调用,为什么呢?

派生类继承自基类,那么基类就只会存在于派生类中,直到派生类调用析构函数后。

假定:基类的析构函数调用比派生类要早,会造成的一种情况就是类成员不存在了,而类本身却还在,但是类存在的情况下,类成员应该还存在。所以这就矛盾了,所以派生类的析构函数会先被调用,基类的析构函数再被调用。

修改一下代码:


#include <iostream>
using namespace std;

class A
{
public:
A()
{
cout <<"A..."<<endl;
}
virtual ~A()
{
cout <<"~A..."<<endl;
}
};

class B :public A
{
public :
B()
{
cout <<"B..."<<endl;
}
~B()
{
cout <<"~B..."<<endl;

}
};

int main()
{
A *a = new B();
delete a;

return 0 ;
}

输出:

A…

B…

~B…

~A…

仅仅只是在基类的析构函数前面加了一个“virtual”,使它成为“虚析构函数”了,这就是“虚析构函数”存在的意义 :)

 

析构函数的作用并不是删除对象,而是撤销对象占用内存之前完成的一些清理工作…

//=========================================

总结:如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,就不要定义虚析构函数了,因为它会增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移值性。

所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。

当且仅当类里包含至少一个虚函数的时候,才去声明虚析构函数。

抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以在想要成为抽象类的类里声明一个纯虚析构函数。

 

定义一个函数为虚函数,不代表该函数未被实现,只是为了来实现多态。

定义一个函数为纯虚函数,才表示函数未被实现 ,定义它是为了实现一个接口,起一个规范作用。继承抽象类的派生类要实现这个函数…

 

看下面的代码,如何输出:


//A.h
#ifndef __A_H__
#define __A_H__

class A
{
public:
A(void);
A(int a, int b);
virtual ~A(void);

virtual void numAdd() = 0;
virtual void f();
void ff();

private:
int a;
int b;
int c;
};

#endif


//A.cpp
#include "A.h"
#include <iostream>

using namespace std;

A::A(void)
{
}

A::A(int a, int b)
{
this->a = a;
this->b = b;

cout<<"a:"<<a<<" b:"<<b<<endl;
}

A::~A(void)
{
cout<<"~A"<<endl;
}

void A::f()
{
cout<<"A::f()"<<endl;
}

void A::ff()
{
cout<<"A::ff()"<<endl;
}


//B.h
#ifndef __B_H__
#define __B_H__

#include "a.h"

class B :public A
{
public:
B(void);
~B(void);

virtual void numAdd();

void f();
virtual void ff();
};

#endif


//B.cpp
#include "B.h"
#include <iostream>

using namespace std;

B::B(void)
{

}


B::~B(void)
{
cout<<"~B"<<endl;
}

void B::numAdd()
{

}

void B::f()
{
cout<<"B::f()"<<endl;
}

void B::ff()
{
cout<<"B:ff()"<<endl;
}

main函数的定义:


//main()
B b;
A * a = &b;

a->f();
a->ff();

return 0;

输出什么?

B::f()

A::ff()

//定义指向基类对象的指针a,当调用f()方法时,因为f为虚函数,所以调用了派生类的f(),输出B::f();

 

参考:

详解C++中的纯虚函数(虚函数区别)&多态性以及实例应用

C++析构函数、构造函数、虚函数关系

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

目录
相关文章
|
2月前
|
算法 API 计算机视觉
[opencv学习笔记] jiazhigang 30讲源码C++版本(含Makefile)
[opencv学习笔记] jiazhigang 30讲源码C++版本(含Makefile)
26 0
|
3月前
|
缓存 网络协议 Linux
Linux C/C++ 开发(学习笔记十三):百万并发的服务器实现
Linux C/C++ 开发(学习笔记十三):百万并发的服务器实现
54 0
|
3月前
|
存储 关系型数据库 MySQL
Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储
Linux C/C++ 开发(学习笔记八):Mysql数据库图片存储
47 0
|
3月前
|
关系型数据库 MySQL 数据库
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
49 0
|
3月前
|
Shell Linux C++
Linux C/C++ 开发(学习笔记二):Shell脚本编程案例
Linux C/C++ 开发(学习笔记二):Shell脚本编程案例
38 0
|
3月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
56 0
|
5月前
|
C++
53 C++ - 虚析构函数
53 C++ - 虚析构函数
19 0
|
3月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
39 0
|
3月前
|
算法 Linux Shell
Linux C/C++ 开发(学习笔记三):Linux C编程案例
Linux C/C++ 开发(学习笔记三):Linux C编程案例
24 0
|
5月前
|
存储 Cloud Native 编译器
C++ 虚析构函数的作用?
C++ 虚析构函数的作用?