-
2021-01-17 17:48:57
析构函数也是一个在类中跟构造函数类似的特殊功能的成员函数,作用与构造函数相反,是在对象的声明周期结束的时候会被自动调用.在C++中析构函数的名字跟类名相同,并在前面带上一个取反的符号~,表达的意思也跟构造函数的过程相反.
默认情况下,如果类没有定义自己的析构函数,编译器会自动为该类生成一个默认的析构函数,只不过函数体是空的,也就是什么都没做.所有,如果需要在对象被删除的时候做一些操作的话,就得自己定义析构函数.
以下几种情况会自动调用析构函数:
①如果一个函数中定义了一个局部变量的对象,那么当这个函数执行结束时也就是该变量对象生命周期结束的时候,所以析构函数会被自动调用.
②全局变量或者static类型的变量,他们的生命周期一般是在程序退出的时候,该对象的析构函数才会被调用.
③如果是用new操作符动态的创建了一个对象,只有当用delete进行释放该对象的时候,析构函数才会被调用.
析构函数的作用:
那构造函数来说,构造函数是新建对象吗?不是,而是在对象被创建出来后自动被调用的,用来初始化相关信息的函数.同理:析构函数也不是用来删除对象的,而是当对象被删除的时候自动被调用的,用来做一些对象被删除之前的清理工作,只有对象的生命周期结束,那么程序就自动执行析构函数来完成这个工作.
析构函数的特点:
析构函数不返回任何值,没有函数类型,也没有任何函数的参数.析构函数不能被重载,所以,一个类中可以有多个构造函数,但只有一个析构函数.
析构函数使用示例:CStudent::~CStudnet() { cout<<"~CStudent() callec."<<endl; }
//本文来源于VC驿站,仅作为个人学习记录
更多相关内容 -
详解C++中的析构函数
2020-12-17 04:29:04析构函数(Destructors),是对象的成员函数,没有返回值也没有参数,且一个类只有一个析构函数,当对象被销毁的时候调用,被销毁通常有这么几个情况。 函数执行结束 程序执行结束 程序块包含的局部变量 delete... -
C++:类的六个默认函数之一 —— 析构函数
2020-03-11 17:33:53文章目录析构函数概念析构函数...而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。 析构函数特性 析构函数是特殊的成员函数。 其特征如下: 析构函数名是在类名前加上字符 ~。 无参数无返回值。 一个...
概念
前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的?
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特性
:
析构函数是特殊的成员函数。其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值。(析构函数不能重载)
- 一个类有且只有一个析构函数。(若未显式定义,系统会自动生成默认的析构函数)
- 对象生命周期结束时,C++编译系统系统(编译器)自动调用析构函数,完成对象中资源的清理。
typedef int DataType; class SeqList { public : SeqList (int capacity = 10) { _pData = (DataType*)malloc(capacity * sizeof(DataType)); assert(_pData); _size = 0; _capacity = capacity; } ~SeqList() { if (_pData) { free(_pData ); // 释放堆上的空间 _pData = NULL; // 将指针置为空 _capacity = 0; _size = 0; } } private : int* _pData ; size_t _size; size_t _capacity; };
- 通过下面的程序我们会看到,编译器生成的默认析构函数,对会自定类型成员调用它的析构函数。
class String { public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~String()" << endl; free(_str); } private: char* _str; }; class Person { private: String _name; int _age; }; int main() { Person p; return 0; }
注意:
类中如果没有涉及到资源管理时,析构函数是否给出无所谓;但是如果涉及到资源管理,用户必须要显式给出析构函数,在析构函数中清理对象的资源。
知识点习题
- 有如下程序,执行后输出的结果是( )
#include <iostream.h> class cla{ static int n; public: cla(){n++;} ~cla(){n--;} static int get_n(){return n;} }; int cla::n= 0; int main() { cla *p =new cla; delete p; cout<<"n="<<cla::get_n()<<endl; return 0; }
A. n=3
B. n=4
C. n=1
D. n=0正确答案:
D
答案解析:
类的实例化:cla *p = new cla,p分配在栈上,p指向的对象分配在堆上。
n为静态成员变量,没有this指针,属于类域,所有对象共享。
实例化——调用构造函数,所以n++;
delete——调用析构函数,所以n–。
最后输仍旧为0。- 如果有一个类是 myClass , 关于下面代码正确描述的是:
myClass::~myClass(){ delete this; this = NULL; }
A. 正确,我们避免了内存泄漏
B. 它会导致栈溢出
C. 无法编译通过
D. 这是不正确的,它没有释放任何成员变量。正确答案
C
答案解析
1.在类A的析构函数中,delete一个非A类对象通常是没有问题的;
在类A的析构函数中,delete一个类A的对象,就会造成死循环,堆栈溢出;
在析构函数外使用 delete后,应该立即给指针赋值 NULL防止野指针。2.因为this是Myclass * const this指针,也就是说this指针指向的对象(不是指向的对象的值)不可以改变,所以给this赋值在编译期间就不会通过,如果没有this = NULL这语句的话是栈溢出,因为会不停的调用析构函数。
3.this被const修饰不能修改。 删掉this=null后,在类的析构函数中调用delete this,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。
- 分析一下这段程序的输出
#include<iostream> using namespace std; class B { public: B() { cout << "default constructor" << " "; } ~B() { cout << "destructed" << " "; } B(int i): data(i) { cout << "constructed by parameter" << data << " "; } private: int data; }; B Play( B b) { return b; } int main(int argc, char *argv[]) { B temp = Play(5); return 0; }
A. constructed by parameter5 destructed destructed
B. constructed by parameter5 destructed
C. default constructor" constructed by parameter5 destructed
D. default constructor" constructed by parameter5 destructed destructed正确答案
A
答案解析
从赋值右边开始执行
调用Play函数需要将5隐式类型转换为Play函数中的形参b,会调用B的B(int i): data(i),打印“constructed by parameter5”。
Play函数返回时需要调用B的拷贝构造函数给对象temp初始化。
Play函数返回后需要调用b的析构函数,将Play函数的形参释放,打印“destructed”。
main函数返回后需要释放temp,打印“destructed”。- 关于以下代码,哪个说法是正确的?
myClass::foo(){ delete this; } .. void func(){ myClass *a = new myClass(); a->foo(); }
A. 它会引起栈溢出
B. 都不正确
C. 它不能编译
D. 它会引起段错误正确答案:
B
答案解析:
delete this(对象请求自杀)是允许的,但是必须保证:
- 1: 该this对象是100%new出来的,并且是最普通的new出来的,不能是new[],定位new等等
- 2: 该成员函数是this对象最后调用的成员函数(因为成员函数第一个参数是隐藏的this指针)
- 3: delete this之后必须保证不能访问到成员变量和虚函数(调用完delete this 之后,对象的内存空间被释放了,导致不能再访问数据成员和虚函数)
- 4: delete this不能放在析构函数中,否则递归 (导致堆栈溢出)
题目中this对象是通过最普通的new产生的,并且之调用了foo成员函数,即保证了foo是最后一个this调用的成员函数,且之后没有访问成员函数和虚函数的操作,因此没有问题。
- 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c; void main() { A*pa=new A(); B b; static D d; delete pa; }
A. A B C D
B. A B D C
C. A C D B
D. A C B D正确答案: B
答案解析
这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配
其中全局变量和静态局部变量时从 静态存储区中划分的空间,
二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 “}” 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 “}” 之前。
如果有不同见解,欢迎留言讨论
-
c++析构函数
2021-01-07 18:55:14在main函数中创建了t0,t1,t2,t3几个对象,这里先说一下C++创建对象的三种不同方式: 1、Test p1(1); //栈中分配内存 2、Test p2 = Test(2); //栈中分配内存,跟方法1相同,是方法1的完整模式 3、Test *p3 = new ...c++析构函数
首先我们来看一下有关析构函数的文字描述
1、定义
析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
2、 作用:对象消亡时,自动被调用,用来释放对象占用的空间
3、特点:
(1) 名字与类名相同
(2) 在前面需要加上"~"
(3) 无参数,无返回值
(4) 一个类最多只有一个析构函数
(5) 不显示定义析构函数会调用缺省析构函数#include<iostream> using namespace std; class Test { int id; public: Test() { } Test(int i) { id = i; } ~Test() { cout<<"ID: "<<id<<" destruction function is invoked!"<<endl; }; }; int main() { Test t0(0); //栈中分配 Test t1[3]={1,1,1}; //栈中分配 Test *t2 = new Test(2); //堆中分配 delete t2; Test *t3 = new Test[3]{3,3,3}; //堆中分配 delete []t3; cout<<"------End of Main-------"<<endl; return 0; }
在给出运行结果以前有必要说一下,我在vs2012的编译器里也运行了这段代码。但是其中
Test *t3 = new Test[3]{3,3,3};
这行代码是有错误的,我去查阅了相关的博客资料发现,在C++11中引进了这一新功能,即用new申请动态数组时可以直接初始化,形式如下:int* p = new int[cnt](); //其中cnt长度和int类型都可以自己定义。 int* p = new int[cnt]{ };
而且可以用这个方式给每个成员赋值。
int* a = new int[10] { 1,2,3,4,5,6,7,8,9,10 };
所以说上面的
Test *t3 = new Test[3]{3,3,3};
语句就应该是没问题的,下面给出运行结果。
解释:
在main函数中创建了t0,t1,t2,t3几个对象,这里先说一下C++创建对象的三种不同方式:1、Test t0(0); //栈中分配内存 2、Test t1[3]={1,1,1}; //栈中分配内存 3、Test *t2 = new Test(2); //堆中分配内存 4、Test *t3 = new Test[3]{3,3,3}; //堆中分配内存
方法1、2中都是在栈中分配内存,在栈中内存由系统自动的去分配和释放,而使用new创建的指针对象是在堆中分配内存,当不需要该对象时,需要我们手动的去释放(delete),否则会造成内存泄漏。
在上述程序中,t0和t1都是栈中的对象,在程序结束时由系统来释放,因此出现在-----End of Main----之后。t2,t3是new出来的堆中对象,所以需要手动的delete释放,因此出现在最前面。另外有一点发现,就是栈中对象的释放顺序,是后定义的先释放,经过几次验证也如此,我想这恰好应征了栈的后进先出的特征。(先释放1,后释放0)
class Test { int id; public: Test(int i) { id = i; } ~Test() { cout<<"ID: "<<id<<" destruction function is invoked!"<<endl; }; }; Test t0(0); //最先创建的对象,最后释放 void Func() { static Test t1(1); //创建静态对象,会在整个程序结束时自动释放 Test t2(2); //在Func结束时自动释放 cout<<"-----Func-----"<<endl; } int main() { Test t3(3); t3 = 10; //类型转换构造函数,这里会创建临时对象,将int型转成Test类型对象, //在赋值结束后,临时变量销毁 cout<<"------Begin of Main-------"<<endl; { Test t4(4); //花括号代表作用域,不需要等到main方法结束就释放了 } Func(); //进入Func函数 cout<<"------End of Main-------"<<endl; return 0; }
那让我们先分析一波 先是t3,t3创建完成以后想要把10赋值过来,但是一个是int类型,一个是Test,这里就会创建临时对象,将int型转成Test类型对象,在赋值结束后,临时变量销毁(把临时创建的对象得10赋值给了t3),销毁临时对象,调用一次析构函数,然后是------Begin of Main-------,到了t4,因为是在花括号里,直接调用了一次析构函数,不需要等到main方法结束,(此处如果没有花括号那么它应该在哪里呢,答案当然是在t3前面 也就是------End of Main-------之后的10前面),到了Func(); 里面的t1和t2,t1静态对象,会在整个程序结束时自动释放,t2在Func()结束时释放,所以先------Func------在调用析构函数,然后------End of Main-------,接着是释放t3,静态t1,最前面最先定义的t0;
-
C++析构函数定义和使用
2021-06-10 20:22:40析构函数(destructor)是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。在C++中“~”是位“取反”运算符,从这一点可以想到:析构函数是与构造函数作用相反的函数。例如:...析构函数
2.1 析构函数定义和使用
析构函数(destructor)是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。在C++中“~”是位“取反”运算符,从这一点可以想到:析构函数是与构造函数作用相反的函数。例如:
class student{
public:
student()//构造函数
{
}
~student() //析构函数
{ }
void print(){}
};
可以看到,定义了一个student类。然后,定义student类的构造函数和析构函数。其中,析构函数的定义如下:
~student(){ }
那么,函数名是类名前面添加取反符号“~”,而且,函数没有参数。
当对象的生命周期结束的时候,就会自动执行析构函数。如果出现以下几种情况,程序就会执行析构函数:
(1) 如果在一个函数中定义了一个对象,那么,该对象就是局部对象,当这个函数被调用结束时,对象的生命周期结束。此时,对象应该释放(销毁),在对象释放(销毁)前自动执行析构函数。
(2) static 局部对象在函数调用结束时对象并不释放(销毁),因此,不调用析构函数,只在main() 函数结束或调用exit()函数结束程序的时候,才调用static局部对象的析构函数。
因为static局部对象的生命周期是整个程序运行过程,所以,只有程序结束运行的时候,static局部对象的生命周期才结束,才执行类的析构函数。
(3) 如果定义了一个全局对象,全局对象的生命周期是整个程序运行过程,所以,当程序结束运行的时候,全局对象才销毁,此时,调用该全局对象的析构函数。
(4) 如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前,完成一些清理工作。使这部分内存可以被程序分配给新对象使用。程序设计者要设计好析构函数,以完成所需的功能,只要对象的生命周期结束,程序就会自动执行析构函数来完成这些工作。
析构函数不返回任何数值,也没有函数类型 和 函数参数。由于没有函数参数,因此,它不能被重载。一个类可以有多个构造函数,但是,只能有一个析构函数。
一般情况下,类的设计者应当在声明类的同时定义析构函数,以指定如何完成“清理”的工作。如果用户没有定义析构函数,C++编译系统会自动生成一个 默认析构函数,但是,它只是有析构函数的名称和形式,实际上什么操作都不进行。想让析构函数完成任何工作,都必须在定义的析构函数中执行。
如下是一个程序测试例子,讲解构造函数和析构函数的使用。
程序运行结果如下:
在main()函数中,定义一个if(1){}代码块,在代码块中定义stud局部变量。当退出代
码块的时候,销毁stud局部变量,看到析构函数的调用。
所以,我们看到对象销毁的时候,自动调用析构函数。析构函数是不可以手动调用的。是对象销毁的时候,由系统自动调用。
所以,对象销毁的时候,我们想释放一些资源,就可以放在析构函数中操作。例如,student类中的name, addr是动态申请的内存。那么,在析构函数中就释放这些内存。
韦凯峰 Linux C/C++ 程序设计教程,Linux 系统编程,Openwrt 系统开发,微信:13926572996,QQ:1523520001,博客:www.mylinux.vip
-
C++析构函数
2019-09-23 08:45:21C++析构函数 一、认识析构函数 析构函数也是一种特殊... 一个类只能有且有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数(合成析构函数)。 因为无参数无返回值析构函数不能重载。每有一次构... -
C++中析构函数为虚函数
2019-04-12 23:13:17(1)析构函数定义为虚函数时:基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全... -
【C++要笑着学】类的默认成员函数详解 | 构造函数 | 析构函数 | 构造拷贝函数
2022-03-08 18:02:17我是柠檬叶子C,本章将继续讲解C++中的面向对象的知识点,本篇主要讲解默认成员函数中的构造函数、析构函数和拷贝构造函数。还是和以前一样,我们将由浅入深地去讲解,以 "初学者" 的角度去探索式地学习。会一步步地... -
为什么基类的析构函数不是虚函数时,就不会调用派生类的析构函数
2015-06-05 07:35:50我知道,基类的析构函数要生命为虚函数,不然用基类指针删除派生类对象时只会调用基类的析构函数,而不会调用派生类的析构函数,从而发生内存泄露。但是为什么会这样?为什么基类析构函数不是虚函数时,就不会调用... -
浅谈析构函数调用次数
2020-03-15 21:29:52这个时候,我们不需要什么操作,析构函数会自动调用 而当对象位于堆区 例如new出来的对象,调用析构函数是在我们delete它的时候被调用的 class A { int v; static int n; public: A ( int n=0):v(... -
C++——构造函数、析构函数以及复制构造函数
2021-03-09 16:28:49文章目录一、构造函数1.1 构造函数是什么?1.2 为什么要有构造函数...C++中,构造函数是一种特殊的成员函数,在每次创建一个类的时候编译器都会默认调用构造函数进行初始化。 1.2 为什么要有构造函数? 构造函数的作用 -
[C++]8.3.4 析构函数的定义与使用
2022-03-28 16:36:36析构函数的作用; 为类定义析构函数; 关于析构函数的几点说明; 复杂对象的构造函数和析构函数调用; 使用构造函数实现强制类型转换。 -
编写类String的构造函数、析构函数和赋值函数
2018-07-27 15:33:26编写类String的构造函数、析构函数和赋值函数,已知类String的函数原型为: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造... -
C++类——派生类的构造函数与析构函数
2018-07-11 17:10:32每个类都分别定义了它的对象被初始化的方式, 类通过一个或几个特殊的成员函数来控制器对象的初始化过程, 这些函数叫做构造函数。 构造函数的任务是初始化对象的数据成员, 无论何时只要类的对象... -
析构函数的一些特点
2017-10-18 20:19:47完成的工作: 类似构造函数有一个初始化部分和一个函数体部分,析构函数也有一个函数体和一个析构部分。构造函数中,先进行成员的初始化,再执行函数体;而在析构函数中,是先执行函数体,后销毁成员。成员按初始化... -
C++构造函数与析构函数及调用顺序
2021-06-07 21:18:39构造函数与析构函数及调用顺序构造函数定义特点析构函数定义特点构造函数与析构函数的执行顺序例题 构造函数 定义 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据... -
C++构造函数析构函数
2015-06-02 21:17:41此word文档主要讲解的是C++的构造函数和洗过后函数。配合几个例子,希望能给初学者一些帮助。 -
构造函数与析构函数的问题总结
2022-04-23 16:42:38问题四:构造函数能否声明为虚函数或者纯虚函数,析构函数呢? 问题五:构造函数、析构函数、可否声明为内联函数inline 问题六:构造函数和析构函数可以调用虚函数吗? 问题七:什么情况会自动生成默认构造函数?... -
析构函数的调用顺序
2020-03-24 10:33:33如果是多个对象,调用构造函数的次序与调用析构函数的次序相反 对于不同作用域和存储类别的对象,构造函数he析构函数的调用顺序 全局对象 构造函数在文件中所有函数执行前调用 当main函数执行完毕或者是调用exit... -
虚基类继承中的析构函数问题
2019-07-04 23:17:42虚基类继承中的析构函数问题 在很多教材里面对虚基类继承的构造函数...实验1:使用派生类指针指向派生类对象,析构函数不加virtual #include <iostream> class A { public: A() { }; ~A() { std::cout ... -
C++继承关于析构函数的问题
2018-06-29 09:50:30析构函数的问题 关于C++中析构函数的作用,不在详述。...导致查了半天的Bug,才发现是少声明了一个析构函数。 首先,存在Bug的代码,大体如下: // Base.h #define USE_EXPROT_ __attribut((... -
C++类的理解(二):函数重载和多个构造函数,以及析构函数
2018-08-14 08:54:35这个函数接受两个整型变量,返回他们的和,但如果我还要一些功能,比如两个double类型的和,一个整型和100的和,并且我也想用add这个函数名怎么办? 函数重载的概念就是用来解决这个问题的,我们... -
析构函数为虚函数
2019-06-20 12:37:14多态是面向对象的一个基本属性,包括静态多态(编译阶段)和动态多态(运行阶段),静态多态主要是指函数参数不同产生的多态性,是在编译阶段可以识别的一种多态机制,而运行时多态则主要用于基类指针指向派生类对象... -
C++的四个默认函数(构造函数,析构函数,拷贝函数,赋值函数)
2021-02-21 17:17:29在C++中,对于一个类,C++的编译器都会为这个类提供四个默认函数,分别是: A() //默认构造函数 ~A() //默认析构函数 A(const A&) //默认拷贝构造函数 A& operator = (const A &) //默认赋值函数。 ... -
c++中的析构函数
2019-04-09 20:55:46印象中c++的析构函数是会自动调用。但是今天运行程序的时候,却没有进入到我写的析构函数,让我不禁怀疑,最终到底析构了没有。还是暗自调用了系统的析构。所以来记载一下析构的知识。 以下属转载,原文博客地址:... -
C++析构函数和delete关系
2020-10-23 16:41:16在栈上的对象,作用域结束后自动调用析构函数(自动的),而delete用于在堆上创建的空间,执行delete后,程序会执行该对象的析构函数,而不执行delete程序不会自动执行析构函数(也就是说,不使用delete它就不析构)... -
析构函数报错
2017-12-20 13:53:23析构函数在什么时候被调用执行? 对于C++程序员来说,这个问题比较简单,但是比较爱唠叨的阿愚还是建议应该在此再提一提,也算回顾一下C++的知识,而且这将对后面的讨论和理解由一定帮助。先看一个简单的示例吧... -
C++ 类 析构函数
2019-03-29 13:10:00一个类最多只能有一个析构函数。析构函数不返回任何值,没有函数类型,也没有函数参数,因此它不能被重载。 构造函数可能有多个,但析构函数只能有一个,就像人来到人世间,可能出生的环境家庭不同(重载构造函数)... -
python析构函数
2018-06-11 17:36:34通常,我们会在析构函数中做一些“清理”工作,比如释放资源,关闭连接,或者发送一个网络包到其它服务以告知其状态变化等等。class Role: def __init__(self, name, role, weapon, life_value=100, money=15000): ...