C++笔记(8)常规new运算符和定位new运算符
通常,new负责在堆(heap)中找到一个能够满足要求的内存块。new运算符还有一种变体,被称为定位(placement)new运算符,他能让你能够指定要使用的位置。程序员可以使用这种特性来设置其内存管理规程、处理需要通过特定地址进行访问的硬件或在特定位置创建对象。
要使用定位new特性,
- 需要包含头文件new,它提供了这种版本的new运算符的原型;
- 将new运算符用于提供所需地址的参数。
下面的代码段演示了new运算符的4种用法:
#include <new>
struct chaff {
char dross[20];
int slag;
};
char buffer1[50];
char buffer2[500];
int main() {
chaff* p1, * p2;
int* p3, * p4;
//常规new运算符
p1 = new chaff;//结构放在堆中
p3 = new int[20];//int数组放在堆中
//定位new运算符
p2 = new (buffer1) chaff;//结构放在buffer1中
p4 = new (buffer2) int[20];//int数组放在buffer2中
return 0;
}
上面这个示例使用两个静态数组来为定位new运算符提供内存空间。接下来使用常规new运算符和定位new运算符创建动态分配的数组。
#include <iostream>
#include <new>
const int BUF = 512;
const int N = 5;
char buffer[BUF];
int main() {
using namespace std;
double* pd1, * pd2;
int i;
cout << "Calling new and placement new:\n";
pd1 = new double[N];
pd2 = new (buffer) double[N];
for (i = 0; i < N; i++) {
pd2[i] = pd1[i] = 1000 + 20.0 * i;
}
cout << "Memory adress:\n" << "heap:" << pd1 << " static:" << pd2 << endl;
cout << "Memory contents:\n";
for (i = 0; i < N; i++) {
cout << pd1[i] << " at " << pd1 + i << ";";
cout << pd2[i] << " at " << pd2 + i << endl;
}
//----------------again---------------------------
cout << "Calling new and placement new a second time:\n";
double* pd3, * pd4;
pd3 = new double[N];
pd4 = new (buffer) double[N];
for (i = 0; i < N; i++) {
pd4[i] = pd3[i] = 1000 + 40.0 * i;
}
cout << "Memory adress:\n" << "heap:" << pd3 << " static:" << pd4 << endl;
cout << "Memory contents:\n";
for (i = 0; i < N; i++) {
cout << pd3[i] << " at " << pd3 + i << ";";
cout << pd4[i] << " at " << pd4 + i << endl;
}
//释放p1指向的内存块,在buffer中分配新的内存
cout << "Calling new and placement new a third time:\n";
delete[] pd1;
pd1 = new double[N];
pd2 = new (buffer + N * sizeof(double)) double[N];
for (i = 0; i < N; i++) {
pd2[i] = pd1[i] = 1000 + 60.0 * i;
}
cout << "Memory adress:\n" << "heap:" << pd1 << " static:" << pd2 << endl;
cout << "Memory contents:\n";
for (i = 0; i < N; i++) {
cout << pd1[i] << " at " << pd1 + i << ";";
cout << pd2[i] << " at " << pd2 + i << endl;
}
delete[] pd1;
delete[] pd3;
}
运行结果:
注意:
1. p2和buffer的地址相同,说明确实将数组p2放在数组buffer中了。p1位于动态管理的堆中。
2. 由于buffer是char指针,将cout运用于char指针时,会从第一个字符开始打印,直到遇到空字符为止。因此使用(void *)对 buffer进项强制转化。
3. 定位new运算符使用传递给它的地址,不跟踪那些内存单元被使用,也不查找未使用的内存块。因此,第二次定位new运算符分配了与第一次相同的内存块。这将一些内存管理的负担交给了程序员。
//偏移量为40bytes
pd2 = new (buffer + N * sizeof(double)) double[N];
4. 用定位new运算符来创建新的类对象后,当该对象消亡时,程序并不会自动地调用其析构函数,所以必须显式地调用析构函数。这个少数的需要显示调用析构函数的情况之一。
上面的程序中没有用delete来释放定位new运算符分配地内存。buffer指定的内存是静态内存,而delete只用用于这样的指针:指向常规new分配的堆内存。也就是说,数组buffer位于delete的管辖区域之外,下面的语句将引发运行阶段错误:
delete [] pd2;//won't work
如果buffer是使用常规new运算符创建的,便可使用常规delete运算符来释放整个内存块。
char * buffer = new char[BUF];
delete [] buffer;
delete [] buffer;释放使用常规new运算符分配的整个内存块,但没有为定位new运算符在该内存块中创建的对象调用析构函数。
这种问题的解决方法是,显式地为使用定位new运算符创建的对象调用析构函数。显式地调用析构函数时,必须指定要销毁的对象。由于有指向对象的指针,因此可以使用这些指针:
String *pd = new (buffer) String("hello",5);
pd->~String();
需要注意的是,对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外,仅当所有对象都被销毁后,才能释放用于储存这些对象的缓冲区。
5. new运算符只是返回传递给它的地址,并将其强制转换为void *,以便能够赋给任何指针类型。
C++笔记(8)常规new运算符和定位new运算符的更多相关文章
- C++ 定位new运算符
这里说的定位new运算符,是一种相对于普通的new运算符,可以指定内存地址的运算符,程序直接使用我们提供的地址,不管它是否已经被使用,而且可以看到新值直接覆盖在旧值上面. 定位new运算符直接使用传递 ...
- PYTHON 100days学习笔记002:语言元素-数字变量与运算符
参考文章: Python 变量类型 Python 运算符 Day02 - 语言元素 1. 指令和程序 计算机的硬件系统通常由五大部件构成,包括:运算器.控制器.存储器.输入设备和输出设备.其中,运算器 ...
- 【读书笔记】C#高级编程 第七章 运算符和类型强制转换
(一)运算符 类别 运算符 算术运算符 + - * / % 逻辑运算符 & | ^ ~ && || ! 字符串连接运算符 + 增量和减量运算符 ++ -- 移位运算符 < ...
- PHP中的运算符---位运算符、递增递减运算符、三元运算符、字符串运算符、数组运算符、类型运算符、错误控制运算符
1.位运算符 位运算符用来对整型数的指定位进行置位,如果被操作数是字符串,则对该字符串的ASCII码值进行操作. 运算类型 运算符 举例 结果 按位与 & $a & $b 将$a 与 ...
- 【&】位与运算符【|】位或运算符之权限控制算法
[&]位与运算符: 按位与运算符"&"是双目运算符. 其功能是参与运算的两数各对应的二进位相与.只有对应的两个二进位均为1时,结果位才为1 ,否则为0.参与运算的数 ...
- JS代码放置位置、变量与数据类型、运算符与逻辑表达运算符
内容简要: 1.JS代码放置位置的问题: 2.变量与数据类型: 3.运算符与逻辑表达式的运算符 我的位置 全局问题:为何在网页推荐位置(一般在<head></head>内部 ...
- C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式
自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级 ...
- C++运算符重载——重载二元运算符
1.重载二元操作符的方法 二元运算符又称为双目运算符,即需要2个操作数的运算符,例如 + - * / 等. 运算符重载可以分为3种方式:类的非静态成员函数.类的友元函数.普通函数. 例如有 2 个操作 ...
- 【Shell脚本学习10】Shell运算符:Shell算数运算符、关系运算符、布尔运算符、字符串运算符等
Bash 支持很多运算符,包括算数运算符.关系运算符.布尔运算符.字符串运算符和文件测试运算符. 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最 ...
- C++重载流插入运算符和流提取运算符【转】
C++的流插入运算符“<<”和流提取运算符“>>”是C++在类库中提供的,所有C++编译系统都在类库中提供输入流类istream和输出流类ostream.cin和cout分别是 ...
随机推荐
- 文本溢出显示省略号css
项目中常常有这种需要我们对溢出文本进行"..."显示的操作,单行多行的情况都有(具体几行得看设计师心情了),这篇随笔是我个人对这种情况解决办法的归纳,欢迎各路英雄指教. 单行 语法 ...
- 对象数组(java)
如果程序需要某个类的若干个对象,例如Student类的10个对象,显然如下声明10个Student对象是不可取的: Student stul, stu2, stu3, stu4, stu5, stu6 ...
- kratos http原理
概念 kratos 为了使http协议的逻辑代码和grpc的逻辑代码使用同一份,选择了基于protobuf的IDL文件使用proto插件生成辅助代码的方式. protoc http插件的地址为:htt ...
- vue+mockjs模拟用户登录接口(高仿书旗)
项目demo:http://39.103.131.74:8888/shuqi
- 龙湖千丁基于 ACK@Edge 的云原生智慧停车系统架构实践
简介: 结合龙湖千丁自研的新版停车云系统以及 ACK@Edge 提供的标准 Kubernetes 服务以及云边一体化协同解决方案,整体来着,边缘部署时间成本由 1 天缩短到 3 小时,将之前的手动升级 ...
- 一个 Blink 小白的成长之路
写在前面 写过blink sql的同学应该都有体会,明明写的时候就很顺滑,小手一抖,洋洋洒洒三百行代码,一气呵成.结果跑的时候,吞吐量就是上不去.导致数据延迟高,消息严重积压,被业务方疯狂吐槽.这时候 ...
- AI和大数据结合,智能运维平台助力流利说提升核心竞争力
简介: 简介:本文整理自数智创新行--智能运维专场(上海站),流利说最佳实践演讲:<基于SLS千万级在线教育平台统一监控运营实践> 作者:孙文杰 流利说运维总监元乙 阿里云智能技术专家 优 ...
- [Py] Failed to import pydot. You must install pydot and graphviz for `pydotprint` to work
当通过常规命令安装 pip install pydot 和 brew install graphviz 之后,在代码中 import pydot 依旧不生效. 比如:在 tensorflow 使用 t ...
- uiautomator2环境搭建+元素定位(安卓)
一.环境搭建 1.安装uiautomator2 在终端使用pip安装即可 pip install uiautomator2 2.安装adb 可参考:https://www.cnblogs.com/li ...
- ITSM2023年十大功能趋势[采和]
总体描述:更加人性化,引入自动化相关的设计和技术,更加实用好用.1. 100%服务目录服务目录必须完全贴合用户方的运维实际开展的 服务清单,而不是想当然的抄书或者臆想!都2023年了,还有完全不着调的 ...