首页 > 系统相关 >C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.8

C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.8

时间:2024-06-07 09:58:49浏览次数:18  
标签:12 buffer JustTesting 运算符 对象 new 12.8 delete

12.5.3 再谈定位 new 运算符
本书前面介绍过,定位new运算符让您能够在分配内存时能够指定内存位置。第9章从内置类型的角度讨论了定位 new 运算符,将这种运算符用于对象时情况有些不同,程序清单12.8使用了定位 new 运算符和常规 new运算符给对象分配内存,其中定义的类的构造函数和析构函数都会显示一些信息,让用户能够了解对象的历史。

#pragma region 12.8 placenew1.cpp
//placenew1.cpp -- new placement new,no  delete

#if 1
#include <iostream>
#include <string>
#include<new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
	string words;
	int number;
public:
	JustTesting(const string& s = "Just Testing", int n = 0)
	{
		words = s; number = n; cout << words << " constructed\n";
	}
	~JustTesting() { cout << words << " destroyed\n"; }
	void Show()const { cout << words << ", " << number << endl; }
};

int main()
{
	char* buffer = new char[BUF];
	JustTesting* pc1, * pc2;

	pc1 = new (buffer)JustTesting;
	pc2 = new JustTesting("heap1", 20);

	cout <<endl<< "memory block address:\n"<<"buffer: "
		<< (void*)buffer << "  heap: " << pc2 << endl<<endl;

	cout << "Memory contents:\n";
	cout << pc1 << ": ";
	pc1->Show();
	cout << pc2 << ": ";
	pc2->Show();

	cout << endl;
	JustTesting* pc3, * pc4;
	pc3 = new(buffer)JustTesting("Bad Idea", 6);
	pc4 = new JustTesting("Heap2", 10);
	
	cout <<endl<< "Memory contents:\n";
	cout << pc3 << ": ";
	pc3->Show();
	cout << pc4 << ": ";
	pc4->Show();

	delete pc2; //free heap1,heap2;
	delete pc4;
	//delete pc1;
	//delete[] pc1;
	delete[]buffer;	//free buffer;
	cout << "Done\n";
	return 0;
}
#endif
#pragma endregion

该程序使用new运算符创建了一个512字节的内存缓冲区,然后使用new运算符在堆中创建两个JustTesting 对象,并试图使用定位 new运算符在内存缓冲区中创建两个JustTesting对象。最后,它使用 delete来释放使用 new 分配的内存。下面是该程序的输出:
在这里插入图片描述

和往常一样,内存地址的格式和值将随系统而异。程序清单 12.8 在使用定位 new 运算符时存在两个问题。首先,在创建第二个对象时,定位 new 运算符使用一个新对象来覆盖用于第一个对象的内存单元。显然,如果类动态地为其成员分配内存,这将引发问题。其次,将 delete用于pc2 和pc4 时,将自动调用为 pc2 和 pc4 指向的对象调用析构函数;然而,将 delete
用于 buffer 时,不会为使用定位 new运算符创建的对象调用析构函数。这里的经验教训与第9章介绍的相同:程序员必须负责管用定位new运算符用从中使用的缓冲区内存单元。要使用不同的内存单元,程序员需要提供两个位于缓冲区的不同地址,并确保这两个内存单元不重叠。例如,可以这样做:

pcl = new(buffer)JustTesting;
pc3 = new(buffer + sizeof (JustTesting))JustTesting("Better Idea", 6)

:其中指针 pc3 相对于pc1的偏移量为 JustTesting 对象的大小第二个教训是,如果使用定位 new运算符来为对象分配内存,必须确保其析构函数被调用。但如何确保呢?对于在堆中创建的对象,可以这样做:

delete pc2;//delete object pointed to by pc2

但不能像下面这样做:

delete pcl;//delete object pointed to by pc1? No!
delete pc3;//delete object pointed to by pc3? NO!

原因在于 delete 可与常规new运算符配合使用,但不能与定位new运算符配合使用。例如,指针 pc3 没有收到 new运算符返回的地址,因此 deletepc3将导致运行阶段错误。在另一方面,指针pc1指向的地址与bufer相同,但buffer是使用new[初始化的,因此必须使用 delete[]而不是 delete来释放。即使buffer是使用new而不是new初始化的,deletepc1也将释放buffer,而不是pc1。这是因为 new/delete 系统知道已分配的512字节块buffer,但对定位new运算符对该内存块做了何种处理一无所知。

该程序确实释放了bufer:
delete []buffer;// free buffer

正如上述注释指出的,deleteⅡbufer,释放使用常规 new 运算符分配的整个内存块,但它没有为定位 new 运算符在该内存块中创建的对象调用析构函数。您之所以知道这一点,是因为该程序使用了一个显示信息的析构函数,该析构函数宣布了“Heap1”和“Heap2”的死亡,但却没有宣布“Just Testing”和“Badldea”的死亡。这种问题的解决方案是,显式地为使用定位new运算符创建的对象调用析构函数。正常情况下将自动调用析构函数,这是需要显式调用析构函数的少数几种情形之一。显式地调用析构函数时,必须指定要销毁的对象。由于有指向对象的指针,因此可以使用这些指针:

pc3->~JustTesting();//destroy object pointed to by pc3
pc1->~JustTesting();//destroy object pointed to by pc1

程序清单 12.9(下一篇文章)对定位 new运算符使用的内存单元进行管理,加入到合适的 delete 和显式析构函数调用从而修复了程序清单12.8中的问题。需要注意的一点是正确的删除顺序。对于使用定位 new 运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外,仅当所有对象都被销毁后,才能释放用于存储这些对象的缓冲区。

标签:12,buffer,JustTesting,运算符,对象,new,12.8,delete
From: https://blog.csdn.net/zhyjhacker/article/details/139517361

相关文章

  • C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.9
    该程序使用定位new运算符在相邻的内存单元中创建两个对象,并调用了合适的析构函数。#pragmaregion12.9placenew2.cpp//placenew2.cpp--newplacementnew,nodelete#if1#include<iostream>#include<string>#include<new>usingnamespacestd;constintBU......
  • 《手把手教你》系列练习篇之12-python+ selenium自动化测试(详细教程)
    1.简介前面文章我们了解了如何获取元素的text属性值,和判断元素是否显示在页面(is_displayed()方法),本文我们来学习下,判断一个控件是否被选中状态、获取页面元素的大小、组合键-全选文字、组合键-退格键删除文本和鼠标右键等练习的内容。2.验证控件是否被选中还是以百度......
  • 运算符重载编程基础
    运算符重载编程基础运算符重载的两种方法定义运算符重载函数名的步骤友元函数实现操作符重载的应用场景友元函数和成员函数选择方法用友元函数重载<<>>操作符友元函数重载操作符使用注意点友元函数案例vector类例如://全局函数完成+操作符重载Complexope......
  • 122. 滑动窗口最大值(卡码网周赛第二十期(23年用友提前批笔试真题))
    122.滑动窗口最大值(卡码网周赛第二十期(23年用友提前批笔试真题))题目描述给定一个整数数组nums和一个整数k,k表示滑动窗口的大小。你需要找出每个滑动窗口中的最大值与最小值的差,并返回这些差的最大值。输入数组的长度为n,1<=n<=10000,数组中的每个元素范围为[-......
  • Q12 LeetCode904 水果成篮
    1.使用HashMap记录键值对2.定义HashMap方式HashMap<Integer,Integer>map=newHashMap<>();3.map.put(key,value);输入键值对4.map.getOrDefault(value,0);获取值,如果没有默认为0;5.map.get(key)取值6.map.size()键值对长度7.map.replace(key,value)替换key的value值 ......
  • BUUCTF-Misc(111-120)
    [UTCTF2020]FileCarving010editor打开发现了一个压缩包解压出来一个ELF文件我们拖到虚拟机去运行一下吧得到flagflag{2fbe9adc2ad89c71da48cabe90a121c0}二维码参考:BUUCTF:二维码-B0mbax-博客园(cnblogs.com)ps修复一下,我也不会,用的大佬的flag{7bf116c8ec254......
  • 「杂题乱刷」AT_abc126_e
    代码康复训练2024.6.6.链接并查集板子。直接看代码。点击查看代码/*Tips:你数组开小了吗?你MLE了吗?你觉得是贪心,是不是该想想dp?一个小时没调出来,是不是该考虑换题?打cf不要用umap!!!记住,rating是身外之物。该冲正解时冲正解!Problem:算法:思路:*/#include<b......
  • ads1299多通道数据格式解析
    一前记ads1299有多种通道格式的芯片。不同的通道,数据是不同的,具体格式怎么样呢?笔者在做产品的时候用到了,这里做一个备注吧。二格式解析其实,产品手册上讲的很清楚。这个格式是有24bit字节的头+24bit的单通道数据x多少通道。比如,需要四通道的数据采集,那就是:24bit+24bi......
  • CF1234F Yet Another Substring Reverse
    CF1234FYetAnotherSubstringReverse状压dp+高维前缀和一个很显然的发现是最长子串长度不会超过字符集。那么如果没有这个操作,是很简单的,我们看看多了这个操作意味着什么。对于一个子串,考虑一次翻转对它的影响。在它内部的翻转肯定是没有意义的;我们一定有一个操作能将任意......
  • AI算力暴增至120TOPS 英特尔Lunar Lake架构解析
    随着下一代AIPC硬件核心LunarLake的发布,英特尔4年5个制程节点演进也逐步迎来富有革命性的时刻。面对AI时代指数级的算力需求增长,英特尔LunarLake,也就是第二代酷睿Ultra平台的CPU+GPU+NPU算力突破到了120TOPS,这将为基于其打造的AIPC赋予更加强劲、高效的AI性能体验。在台北电......