关于为使用定位new运算符声明的类对象指针调用析构函数时出现的三个问题

告别混乱代码!在Arduino IDE中管理多文件项目的5个必备技巧 本文分享了在Arduino IDE中管理多文件项目的5个必备技巧,帮助开发者告别混乱代码。通过代码分割和模块化设计,提升编译效率、降低内存占用,并实现功能解耦。重点介绍了头文件设计、CPP文件实现、调试技巧和工业级目录结构,让你的Arduino项目像乐高积木一样清晰易维护。 阅读详情

前面学习到通过new运算符创建的对象指针只有通过显式的使用delete才能调用析构函数。那如果对象指针使用的是定位new运算符,要如何确保析构函数被调用呢?看以下例子:

#include<iostream>
using namespace std;

class A
{
private:
	int num;
public:
	A(const int n) { num = n; cout << num << " constructed" << endl; }
	~A() { cout << num << " destroyed." << endl; }
};

int main(void)
{
	char* buffer = new char[512];
	A* pa1 = new(buffer) A(1);
	A* pa2 = new(buffer) A(2);

	delete pa1;//成功释放
	delete pa2;//释放失败
}

运行结果:

1 constructed
2 constructed
2 destroyed.
-572662307 destroyed.

触发断点:HEAP[placenew1.exe]: Invalid address specified to RtlValidateHeap( 01590000, 0159E3E0 )

1.第一个问题,为什么pa2会释放出错?

pa2释放失败的原因是因为二者指向的地址是同一个,pa1已经将这块空间释放了,pa2再释放一次就会出错。

事实上,delete pa1等效于delete buffer,也因此而释放成功了,但这样做是不安全的,一是会出现例子中后续delete的失败,二是delete pa1等效于delete buffer,也就是说只释放了首地址。

正确的做法应该是
delete []buffer;

这其实说明了一个规则,就是delete要释放的是由new开辟的空间,而对于pa1、pa2来说,使用定位new运算符并没有开辟空间,因此使用delete释放就是错误的。

修改后的代码:

#include<iostream>
using namespace std;

class A
{
private:
	int num;
public:
	A(const int n) { num = n; cout << num << " constructed" << endl; }
	~A() { cout << num << " destroyed." << endl; }
};

int main(void)
{
	char* buffer = new char[512];
	A* pa1 = new(buffer) A(1);
	A* pa2 = new(buffer) A(2);

    delete[] buffer;
}

运行结果:

1 constructed
2 constructed

2.这里就出现了第二个问题,虽然内存空间被顺利释放了,但是析构函数没有被调用。

原因在本文第一句话提到了,由new运算符声明的指针需要显式的delete才能调用析构函数,但现在delete定位new运算符声明的指针又会出错,怎么办呢?

解决办法就是显式的调用析构函数,这也是少有的几个需要显式调用析构函数的情况:

#include<iostream>
using namespace std;

class A
{
private:
	int num;
public:
	A(const int n) { num = n; cout << num << " constructed" << endl; }
	~A() { cout << num << " destroyed." << endl; }
};

int main(void)
{
	char* buffer = new char[512];
	A* pa1 = new(buffer) A(1);
	A* pa2 = new(buffer) A(2);

	pa2->~A();//注意后创建的先释放,因为有时候后创建的对象可能使用了先创建对象的资源
	pa1->~A();
	delete[] buffer;
}

运行结果:

1 constructed
2 constructed
2 destroyed.
2 destroyed.

3.最后一个问题,为什么两次析构函数都是2 destroyed?

原因在于pa2使用定位new运算符的起始位置与pa1相同,因此后创建的pa2把pa1的内容挤掉了,代码更改:

#include<iostream>
using namespace std;

class A
{
private:
	int num;
public:
	A(const int n) { num = n; cout << num << " constructed" << endl; }
	~A() { cout << num << " destroyed." << endl; }
};

int main(void)
{
	char* buffer = new char[512];
	A* pa1 = new(buffer) A(1);
	A* pa2 = new(buffer + sizeof(pa1)) A(2);

	pa2->~A();
	pa1->~A();
	delete[] buffer;
}

运行结果:

1 constructed
2 constructed
2 destroyed.
1 destroyed.

VMware Converter 6.2.0保姆级教程:从物理机到ESXi主机的完整迁移指南(含常见错误排查) 本文提供VMware Converter 6.2.0从物理机迁移至ESXi主机的完整保姆级教程。详细解析了迁移前的环境规划、核心转换过程步骤、版本差异对比,并重点针对代理安装失败、任务卡顿、虚拟机无法启动等常见错误提供了实战排查与解决方案,确保P2V迁移顺利进行。 阅读详情

相关推荐

保姆级教程:用Python+NumPy手把手模拟光学散射与记忆效应(附代码)

本文提供了一份详细的Python+NumPy教程,手把手教你模拟光学散射与记忆效应。从基础光波建模到散射介质实现,再到菲涅尔衍射和光学记忆效应验证,逐步构建完整的仿真系统。通过附带的代码示例,读者可以快速掌握计算光学仿真的核心技术,为医学成像、物理仿真等领域的研究提供实用工具。

weixin_30172941的博客 324

c++ 的 指针析构 问题

c++的指针析构问题

ken2232的博客 2162

第055篇:陆地观测卫星数据服务平台高级检索方法

检索数据检索到肩膀疼,这谁能信?谁能?

liujinli0712的博客 7823

new的几种用法

<br />(1)new 运算符 用于创建对象和调用构造函数。 <br />  (2)new 修饰符 用于隐藏基类成员的继承成员。<br />  (3)new 约束用于在泛型声明中约束可能用作类型参数的参数的类型。<br /> <br />  new 运算符<br />  1.用于创建对象和调用构造函数<br />  例:Class_Test MyClass = new Class_Test();<br />  2.也用于为值类型调用默认的构造函数<br />  例:int myInt = new int(

abigale1011的专栏 1622

k8s 查看cpu使用率最高的pod

在 Kubernetes 中,可以使用命令查看 Pod 的资源使用情况,从而找到 CPU 使用率最高的 Pod。

u010674101的专栏 3344

JLink版本不兼容?手把手教你解决APM32F003F6P6在Keil中烧写闪退和报错

本文详细解析了APM32F003F6P6在Keil中烧写闪退和报错的解决方案,重点介绍了JLink版本兼容性问题。通过实测不同版本组合,推荐使用JLink 7.20a或6.86b与Keil 5.25的黄金组合,并提供详细的降级驱动和工程配置步骤,帮助开发者高效解决烧写困境。

weixin_33694620的博客 416

【C++】如果声明一个对象指针时,会不会调用构造函数?

C++中如果声明一个对象指针时没有分配内存,那么不会调用构造函数 一个指针在内存中也是有内存空间的。 在现在大多数机器上指针都是32位的,也就是4个字节。 如果你声明指针。例如,假设A是一个类 A *pa; 这内存中会分配4个字节的空间存储一个地址。只不过地址是不可以用的,因为它没有有效的指向某一个有用的内存空间。这和你声明一个int类型是一样的。 int p; 内存中也会分配4个字节空间给p来存.........

Erqi Huang的博客 6854

让 ABAP 复杂内表优雅“喂饱”:Complex Internal Table Filling 全面实战指南

本文为ABAP开发者系统梳理了复杂内表的填充与复制方法。重点介绍了work area的两种声明方式、APPEND操作的多形态用法及其初始值语义,以及VALUE #( )构造器的逐行与整表写法。同时讲解了CORRESPONDING的同名字段映射功能,帮助开发者在"航司与航线"等复杂业务场景中实现最佳实践。文章还涵盖内存优化、可读性建议和常见误区,提供可运行样例,适用于ABAP On-Premise和SAP BTP环境。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 168

声明对象指针,调用构造、析构函数的多种情况

以下分析一下,声明对象指针,调用构造、析构函数的多种情况,定义以下的一个基类与派生类。 class CBase{ public: CBase(){ cout << "CBase()" << endl; } ~CBase(){ cout << "~CBase()" << endl; } }...

weixin_30606669的博客 681

Halcon 形态学 开发案例 (开运算opening(先腐蚀再膨胀)、闭运算closing(先膨胀再腐蚀))

算子方法:功能:使用用户自定义的结构元素对区域进行开运算参数: Region(输入参数) :输入区域 StructElement(输入参数) :结构元素(区域) RegionOpening(输出参数) :开运算后的区域功能:使用圆形结构元素对区域进行开运算参数: Region(输入参数) :输入区域 RegionOpening(输出参数) :开运算后的区域 Radius(输入参数) :圆形结构元素的半径功能:使用矩形结构元素对区域进行开运算。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 1618

计算机组成原理(第三版)唐朔飞-第八章CPU的结构和功能-课后习题

8.1CPU有哪些功能?画出其结构框图并简要说明每个部件的作用。

weixin_45735391的博客 6万+

C++ 容器存储对象时,指针调用析构函数触发的一系列BUG

今天给大家分享一篇BUG文章,请耐心看完,也许你以后也会遇到这样的BUG而解决不了! 需求是这样的: 定义一个Student类,里面有私有成员整型和指针! 例如:int age; char *name; 我们需要把类定义的对象存入容器中。 其中会发生一系列好玩的事情,拭目以待! 不懂容器的可以点击下面去学习: list容器 deque容器 vector容器 我们这里以vector为例子说明,其他...

cpp_learner的博客 1551

想重装OpenClaw?先执行这4步彻底卸载,避免配置冲突

在重装OpenClaw之前,彻底卸载是非常重要的一步。如果不彻底卸载,可能会导致: - 残留的配置文件与新安装的版本冲突 - 旧的依赖包干扰新的安装过程 - 服务冲突导致启动失败 - 数据混乱影响系统稳定性

一键难忘的博客 1043

C++new与delete的使用

C++的new与delete 1.new的运算符用法(关键字) 2.new的函数用法 3.定位new 4.new创建对象 5.delete 6.对于内置类型new/delete/malloc/free可以混用 7.使用注意事项

Mi-ronin的博客 1万+

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++ —— 指向对象的指针

C++程序经常使用指向对象的指针,通常会搭配 new 来使用,例如,Class_Name 是类,value 是 Type_Name 类型: Class_Name * ptr = new Class_Name(value); 该语句会调用如下的构造函数: Class_Name(Type_Name &); 而下面的初始化则是会调用默认构造函数: Class_Name * ptr = new Class_Name; 再谈 new 和 delete 对于类对象我们可以在两个层次上使用 new 和 de

柚咖小窝 4382

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

使用指向对象的指针和定位new运算符

使用指向对象的指针和定位new运算符

m0_57721815的博客 509

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

使用指针对象的指针(C++)

再谈new和delete: 对于下面的这个例子: String *favorite = new String(sayings); 其中,sayings为String类的对象,这个例子中,这不是为要存储的字符串分配内存,而是为对象分配内存;也就是说,为保存字符串地址的str指针和len成员分配内存。创建对象将调用构造函数,后者分配用于保存字符串的内存,并将字符串的地址赋给str。然后,当程序不再...

MoooLi的博客 1603

定位new 运算符

定位new   2007-10-08 18:03:02|  分类: 技术总结|举报|字号 订阅 在预先定义的内存位置构造一个对象     常常有人问这样一个C++问题:如何在预先定义的内存位置构造一个对象?在预先定义的内存缓冲构造一个对象有许多有用的应用。例如,一个定制的垃圾搜集器能使用一个大的预 分配内存缓冲,用户在这个

hnust_疯子 1381

C++使用指向对象的指针

一、使用new初始化对象 通常,如果Class_name是类,value的类型为Type_name,则下面的语句: Class_name *pclass = new Class_name(value); 将调用如下的构造函数: Class_name(Type_name); 这里会有一些琐碎的转换,如: Class_name(const Type_name &); 另外,如果不存在二义性,则将发生由原型匹配导致的转换(如int转为double)。 下面的初始化方式将调用默认构造函数: Class

xiaogangjava的博客 1455

c++内存管理new和delete运算符

我们先了解计算机中的内存在使用中被分为下面几部分: 内存布局 栈:也叫堆栈,系统自动分配的空间,只要不特殊声明,系统自动在栈上给变量和函数分配内存。栈是自顶向下增长的 堆:可使用动态内存分配的方式申请堆空间,当数据量较大时,栈空间有限,可在堆上申请空间存放;用完需要手动释放! 常量区:用于存放程序中使用到的常量 代码区:存放可执行代码和只读常量 作为开发者,我们在程序中只需要管理的就是内存中的堆...

博客小站 410

【C++私房菜】new/delete、定位new及内存分配

C++内存管理以及new delete ,定位运算符……

无敌岩雀的博客 1578

Java中new的作用

Java中创建一个对象,例如 Person person = new Person("张三", 20); 包括几部分 1. 声明(Declaration):将变量名称与对象类型关联的变量声明。 Person person 就是告诉编译器你将使用person引用一个Person类型的对象。其初始值将待定,直到有一个对象真正被创造和分配给它。这里只是简单地声明一个引用变量而并没有创建一个对象。 2. 实例化(Instantiating):new关键字是一个java运算符,它用来创建...

shanhejiesui的博客 1万+

C++_类和动态内存分配5-使用指向对象的指针

再探new和delete new为创建的每一个对象的名称字符串分配存储空间,这是在构造函数中进行的; 析构函数使用delete来释放这些内存。 字符串是一个字符数组,所以析构函数使用的是带中括号的delete。 使用new来为整个对象分配内存: String * favorite = new String(saying[choice]); 这不是为要存储的字符串分配内存,而...

weixin_33779515的博客 300

C++ new, delete

new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,即 new会返还一个指针,该指针指向一段空间的首地址。

Dlrow Olleh~ 657

[C++] C++中使用new和delete动态内存分配

      在C语言中,我们经常使用char数组来表示一个字符串,如char name[50]表示申请50个字节的内存空间来存储姓名,然而,这种做法有很多缺陷,比如如果有些人姓名长度大于50,那么就要修改内存大小,这样造成了很大的内存浪费。因此,C语言的做法是通过指针来解决这个问题,比如:char * name;//声明char型指针 //申请内存 name = (char *)malloc(si...

FightFightFight的博客 3863

使用new运算符进行动态内存分配

然⽽,它 们的类型不同,p1是double指针,⽽buffer是char指针(顺便说⼀句,这 也是程序使⽤(void *)对buffer进⾏强制转换的原因,如果不这样做, cout将显⽰⼀个字符串)同时,常规new将数组p1放在很远的地⽅,其 地址为006E4AB0,位于动态管理的堆中。定位new运算符使⽤传递给 它的地址,它不跟踪哪些内存单元已被使⽤,也不查找未使⽤的内存 块。new可能找不到请求的内存量。如果buffer是使⽤常规new运算符创建的,便可以使⽤常 规delete运算符来释放整个内存块。

weixin_45502173的博客 794
上一篇: 如何打印字符串指针的地址?
下一篇: 多态与虚函数
Kallou
博客等级 码龄9年 19粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值