-
2021-04-02 17:08:30
答案是可以。
但是有个误区要注意。虽然可以手动调用析构函数,但是对象的内存并没有释放。看一下代码示例:
#include "stdafx.h" #include <iostream> #include <crtdbg.h> #ifdef _DEBUG #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) #endif using namespace std; class A { public: A() { cout << "构造" << endl; }; ~A() { cout << "析构" << endl; }; virtual void func() {}; char c; }; int main() { //检测内存泄漏,在vs输出窗口调试状态下,可以显示内存泄漏的代码位置 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); A* p = new A; p->~A(); // delete p; return 0; }
调试上面的代码会发现,存在内存泄漏,因为A类型的对象没有释放内存。
这里的误区就是不明确析构函数的作用。虽然在使用delete时会调用析构函数,但是内存的释放不是由析构函数完成的。
析构函数和构造函数的作用是,在使用new时,new会先分配内存,然后调用构造函数。调用delete时,会先调用析构函数,再释放内存。因此,析构函数只是会在调用delete时被执行,但释放内存并不是析构函数完成的,而是delete完成的,因此,销毁对象时要用delete,而不是直接调用析构函数。手动调用析构函数是可以的,但是没太大意义。
更多相关内容 -
C++主动调用析构函数分析
2017-12-05 19:41:49C++编程规范中都不支持显示的调用析构函数,部分文章中甚至说明析构函数是不能显示调用的,然而执行如下类似的操作编译器并不会报错,而且会调用成功。 pa->~A(); 显示调用析构函数有会引起什么问题?为什么C++标准...1、 关于主动调用析构函数;C++编程规范中都不支持显示的调用析构函数,部分文章中甚至说明析构函数是不能显示调用的,然而执行如下类似的操作编译器并不会报错,而且会调用成功。
pa->~A();
我们先来看看这样的操作会有哪些副作用,然后再讨论存在的意义;
1. 如果在析构函数中显示地释放了堆上的内存,显示调用析构函数会存在重复释放内存错误,例子如下:
class A
{
char *a;
public:
A(){a =(char *)malloc(4);}
~A()
{
free(a);
printf(“Adeconstructor\n”);
}
};
int main()
{
A a;
a.~A();
return0;
}
2. 显示的调用析构函数相当于执行了析构函数内容(包括子类析构函数),但是并没有释放内存,也没有摧毁对象;上述的例子可以理解为a对象占用了char *大小的栈内存,占用了4个字节的堆内存,堆内存通过构造函数申请,通过析构函数释放;显示的调用析构函数执行了析构函数,释放掉了堆上的内存,但是并不会释放栈上内存。
3. 使用new操作符的情况有写不同,new操作符可以考虑为两个阶段。
a. 在堆上申请对象本身成员所占内存;
b. 执行构造函数。
考虑如下例子:
class A
{
char *a;
public:
A(){a =(char *)malloc(4);}
~A()
{
free(a);
printf(“Adeconstructor\n”);
}
};
int main()
{
A *a =new A();
a->~A();
return0;
}
显然此时编译器不会隐式调用析构函数,所以不会出现重复释放的问题,但是由于a->~A()只会执行析构函数,a指针本身所占的内存不会被释放掉,所以会造成内存泄漏。
由以上的分析可知,显示调用析构函数不但不会带来任何好处,还会造成很多奇怪、难以分析的问题,这也是不推荐使用的原因,为什么C++标准不禁止呢?来看一下关于placementnew的用法。
placement new是operator new的一个重载版本;所谓placement new就是在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。
例子如下:
struct A {…};
struct B {…};
A* pa = new A;
pa->~A();
B* pb = new (pa) B;//placement new
以上例子必须要求sizeof(A)>=sizeof(B)。B* pb = new (pa) B并不会申请内存,只是在原来A对象的位置上生成B的对象。
更普遍的使用方法如下:
class A{
…
public:
show();
};
1)分配内存
char* buff = new char[ sizeof(A) *N+sizeof(int)) ];
memset( buff, 0, sizeof(A)*N +sizeof(int));
2)构建对象
A* pa = new (buff)A;
3)使用对象
pa->show();
4)析构对象,显式的调用类的析构函数。
pa->~A();
5)销毁内存
delete [] buff;
对于buff这块内存可以反复使用,只要重复2)、3)、4)步骤即可。在C++标准中,对于placementoperator new []有如下的说明:placementoperator new[] needs implementation-defined amount of additional storage tosave a size of array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。
对于效率和容错要求比较高的程序来说placement new是很有用的:
1) placement new能够有效的解决内存碎片的问题;
2) malloc、new等申请内存有失败的可能,对有些程序来说是不允许的,而placementnew可以做到;
3) placement new不需要执行申请内存的过程,速度更快;
C++标准STL就有使用placement new的情况,不过不是在特殊情况下比如自己编写高效率库、需要自己管理内存池时不推荐使用placement new。
-
析构函数的调用
2020-10-07 21:22:41析构函数的调用析构函数是当对象要被释放掉时才会被调用,所以常被用来处理数据,例如释放堆区空间。
由此,可以得出析构函数释放时的三种情况:
1.正常情况
例:
#include <iostream> using namespace std; class Cstu { int a; Cstu():a(22) { cout << a << endl; a = 23; } ~Cstu() { cout << a << endl; } }; int main() { { Cstu stu; } system("pause"); return 0; }
对象stu处于一个局部域内,当执行到22行的花括号时,执行析构函数,可用断点和控制台上输出的数值来验证。
2.处于带返回值函数的内
"return"相当于宣告函数已经执行结束,当执行到这一语句时,便会调用析构函数。
可用上述例子,减去"Cstu stu"两侧的花括号去做实验。
3.指针对象/堆区对象
即:
Cstu *p = new Cstu; delete p;
因为delete的作用便是释放堆区空间,所以执行到delete时执行析构函数。
4.临时对象
Cstu();
因为临时对象的作用域只是其所在的这条语句,所以当语句执行完毕前会调用析构函数。
另:1.只有new 和 delete 会分别触发构造函数和析构函数,malloc 和 free无法触发。
2.析构函数的形式只能是"~Cstu()",不能有参数,所以也无法构成重载函数。
3.当没有写析构函数时,系统会执行默认的析构函数,其内容为空。
-
关于C++手动调用析构函数的理解
2018-11-19 19:06:07以前从来没有自己手动调用过析构函数,这里明显是要根据类型来调用析构函数啊。 踩个坑,可能理解的也没有很透彻 手动调用析构函数的小例子 参考别人的,写了个测试类。 class testDes { public: testDes() ...今天用C++改写jept_json的时候遇到了一个问题。
首先,在lept_json中,在lept_value中定义了一个联合struct lept_value { union { struct { char* s; size_t len; }s; /* string: null-terminated string, string length */ double n; /* number */ }u; lept_type type; };
一开始C++中也想这么写,想想不用自己建栈了美滋滋啊
union { std::string s; double n; };
还顺便用了下匿名union,以后访问再也不用v->u.n了。
然后编译器就报错啦!
构造函数是已删除的函数?
回忆了一下C++ Primer里讲的,这里也没有成员没有默认构造函数呀?
想了下,这个问题也只可能是union引起的。查了查果然是,由于union里有类类型,因此编译器就懵逼了,不知道调用谁的构造函数。
自己手动添加构造函数和析构函数吧。
构造函数容易解决,在构造阶段,直接吧type置为TYPE_NULL就可以了。
略麻烦的是析构函数。以前从来没有自己手动调用过析构函数,这里明显是要根据类型来调用析构函数啊。
踩个坑,可能理解的也没有很透彻手动调用析构函数的小例子
参考别人的,写了个测试类。
class testDes { public: testDes() { //pi = new int(42); cout << "cons" << endl; } ~testDes() { //delete pi; cout << "des" << endl; } string s = "sssssss"; vector<int> ivec{ 1,2,3 }; int i = 0; //int *pi; };
//main.cpp testDes t; cout << t.s << endl; cout << t.i << endl; for (auto i : t.ivec) cout << i << " "; cout << endl; //cout << *t.pi << endl; t.~testDes(); cout << t.s << endl; cout << t.i << endl; for (auto i : t.ivec) cout << i << " "; cout << endl; cout << t.ivec.size() << endl;
运行结果:
在上面的代码状态下,当类内没有int* pi
,也就是没有堆区数据的时候,尽管析构函数被调用了两次,但是没有什么问题。类内的string和vector在第一次调用析构函数后数据就被清理了,但是还在栈区占有空间。int没有收到影响,可以第二次调用。
但是如果去掉注释,第二次delete pi
肯定就会报错了,因为不能把一块空间释放两次。(其实可以delete完了pi=nullptr
)。所以,可以根据类型判断,调用union的对应类型的析构函数。
-
Python中析构函数的调用
2020-07-15 11:58:14Python中析构函数的调用 如果不删除类,则不会调用析构函数。 当删除类时,析构函数被调用。 -
构造函数、类成员、析构函数调用顺序
2022-06-29 20:10:11构造函数、类成员、析构函数调用顺序 -
成员变量析构:对象会主动调用析构函数
2018-09-05 15:30:00#include <iostream> #include <list> class destructor_test{ public: ~destructor_test(){ std::cout << "~destructor_test" << std::endl; } }; ...pri... -
第二十节 C++- 析构函数的使用及调用
2018-03-28 17:13:42析构函数是类的一种特殊函数,只有在对象被销毁时才被调用,在析构函数内,可以对堆内存进行释放,如new(构造函数),delete(析构函数)。构造函数可以有多个(重载),而析构函数只有一个(不能对析构函数进行... -
c++析构函数的调用
2018-01-07 21:23:08//方法结束时,由系统自动调用析构函数释放对象 } int _tmain( int argc, _TCHAR* argv[]) { test(); system( "pause" ); return 0 ; } 上面代码块执行结果: 接着,我们再看一个... -
C++析构函数的自动调用(用于父类指针指向子类对象,内存泄漏问题)(转)
2021-09-03 14:01:25} 执行结果(正常情况,子类祖先类的析构函数都是虚拟的,这样删除祖先类指针、子类对象的时候,可正确同时调用子类和祖先类的析构函数): A B ~B ~A 但是如果去掉 virtual(这里是A*指针指向B对象),改一下立刻... -
Qt窗口关闭和应用程序停止是否调用析构函数的一些说明
2021-06-12 18:56:52这几天一直在模仿QQ做一个即时通讯软件,过程不是很顺利,表现...main函数中,在栈上创建的窗口,关闭的时候自动调用析构函数,这种情况不可以使用this->setAttribute(Qt::WA_DeleteOnClose);,关闭时会出现异常,如 -
C++析构函数的自动调用问题
2019-08-11 10:09:00首先要明确一点,系统只会自动释放栈内空间,而堆内...当单纯的创建对象的时候,对象存放在栈中,此时在程序块的}后面,系统会自动调用析构函数,释放掉栈空间。 但是,如果创建了指向new来的一块空间的指针的时候... -
析构函数何时被调用
2021-03-14 22:28:16析构函数何时被调用析构函数在下边3种情况时被调用:对象生命周期结束,被销毁时;主动调用delete ;对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。第一种情况#include using namespace std;... -
C++——析构函数什么时候被调用
2020-09-07 10:25:293.对象A是对象B的成员,B的析构函数被调用时,对象A的析构函数也被调用。 举例来说: class A { public: A() {ma = new int(1000);} A(size sz) {ma = new int(sz);} virtual ~A() { delete ma;} in... -
程序结束不会自动调用堆对象的析构函数
2021-08-04 19:36:50使用new申请的对象,如果不主动delete掉,在程序运行结束的时候系统释放会调用对象的析构函数呢?我们写个小程序试试 #include <iostream> #include <string> class A { public: A(std::string ... -
php析构函数什么时候调用
2021-03-23 20:06:36析构函数何时被调用析构函数在下边3种情况时被调用:对象生命周期结束,被销毁时;主动调用delete ;(推荐学习:PHP编程从入门到精通)对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。PHP 5 ... -
析构函数能直接被调用,而构造函数不能直接调用
2020-10-29 10:02:40析构函数能直接被调用,而构造函数不能直接调用 在进行面向对象程序设计的时候,我们经常要设计自己的类...//程序说明直接调用构造函数会出现错误,直接调用析构函数是成功的。 class A { public: int id; ... -
C++析构函数和delete关系
2020-10-23 16:41:16在栈上的对象,作用域结束后自动调用析构函数(自动的),而delete用于在堆上创建的空间,执行delete后,程序会执行该对象的析构函数,而不执行delete程序不会自动执行析构函数(也就是说,不使用delete它就不析构)... -
C++ 析构函数与内存回收
2018-11-28 11:28:58在使用caffe和yolo的c++接口时发现: ...在类的对象作为函数参数时,需要作为引用变量,这样在子函数运行结束时才不会重复释放(由于对象在main函数里初始化,main函数结束时会自动调用对象的析构函数)对象的内存。... -
C++中析构函数的作用
2021-02-01 17:30:47析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统 会自动执行析构函数。 析构函数名也应与类名相同,只是在函数名前面加一个位取反符,例如stud( ),以区别 于构造函数。它不能带... -
C++学习——构造函数,析构函数与虚函数关系
2021-06-20 16:01:03构造函数,析构函数可以为虚函数吗?2.析构函数和构造函数的作用?3.构造函数和析构函数调用顺序? 1.构造函数,析构函数可以为虚函数吗? 构造函数不可以,析构函数可以 从存储空间角度,虚函数相应一个指向... -
关于C++析构函数什么时候执行的问题
2022-01-16 22:25:24析构函数有2种方式调用,一个是主动调用delete,另一个是根据RAII的规则,在对象的生命周期结束时自动析构。第一点没什么好说的,就简单讲下第二点。 对象的生命周期不等于是程序的生命周期,一般我们所谓的生命... -
php析构函数什么时候调用?
2019-09-21 12:36:46析构函数何时被调用 析构函数在下边3种情况时被调用: 对象生命周期结束,被销毁时; 主动调用delete ;(推荐学习:PHP编程从入门到精通) 对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用... -
析构函数 (C++)
2018-08-23 09:48:00最近发现自己对析构函数的认知有一定的...不过昨天突然在想,那如果是派生类的话,主动调用析构函数的话,基类的内容怎么回收呢。于是,发现了自己长期以来的基础错误,也说明了自己还是需要不断巩固基础啊。话不多... -
析构函数和delete
2020-07-21 17:54:32一个类只能有一个析构函数,很多时候都是系统隐式调用对对象进行销毁,程序员也无法显式调用,(但可以在析构函数里写上自己想要在销毁的时候需要进行的别的工作,比如: /用来计数雇员数量程序/ Employee(std::... -
析构函数是否必须为虚函数?什么情况下才应该定义析构函数为虚函数?
2020-04-04 10:44:06静态多态主要是指函数参数不同产生的多态性,是在编译阶段可以识别的一种多态机制,而运行时多态则主要用于基类指针指向派生类对象时,可以通过基类指针直接调用派生类的对象函数,当然这种多态是通过虚函数实现的。... -
(4)C++析构函数、两种申请释放空间的不同
2021-05-07 09:50:51析构函数是对象在释放时(对象生命周期结束时)自动调用一个函数 可用断点查看 1.作用: 清理工作:比如我们在堆区(用new)申请了一段空间,析构函数可以释放掉;调用时间决定了它主要的作用 2.形式: ~类名(){} 如下:...