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分别是 ...
随机推荐
- redis和memcached的区别和使用场景
Redis 和 Memcached 都是基于内存的数据存储系统.Memcached是高性能分布式内存缓存服务,其本质上就是一个内存key-value数据库.Redis是一个开源的key-value存储 ...
- (react)获取json数据与传入(antd配合)
import React from 'react'; import {fetch} from 'whatwg-fetch'; // import {HashRouter as Router,Route ...
- 力扣580(MySQL)-统计各专业人数(中等)
题目: 一所大学有 2 个数据表,分别是 student 和 department ,这两个表保存着每个专业的学生数据和院系数据. 写一个查询语句,查询 department 表中每个专业的学生人数 ...
- 力扣153(java&python)-寻找旋转排序数组中的最小值(中等)
题目: 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:若旋转 4 次,则可以 ...
- ACK One 构建应用系统的两地三中心容灾方案
简介:本文侧重介绍了通过 ACK One 的多集群应用分发功能,可以帮助企业管理多集群环境,通过多集群主控示例提供的统一的应用下发入口,实现应用的多集群分发,差异化配置,工作流管理等分发策略.结合 ...
- 如何构建企业出海的"免疫力"?深入解读阿里云CDN安全能力
简介: 随着信息技术快速发展与应用,产业数字化和智能化趋势正日益加深,企业信息安全与防护被提升到前所有未有的高度.阿里云CDN经过10多年的技术发展时间,已逐步构筑一个边缘+云的安全网络立体防护体系, ...
- MySQL深潜|剖析Performance Schema内存管理
简介: 本文主要是通过对PFS引擎的内存管理源码的阅读,解读PFS内存分配及释放原理,深入剖析其中存在的一些问题,以及一些改进思路. 一 引言 MySQL Performance schema(PF ...
- [FAQ] 适用于 macOS / Arm64 (M1/M2) 的 VisualBox
使用与 Windows.Linux.macOS 的x86架构的一般在下面地址中下载: Download VisualBox:https://www.virtualbox.org/wiki/Down ...
- 鸿蒙HarmonyOS实战-ArkUI事件(触屏事件)
前言 触屏事件是指通过触摸屏幕来进行操作和交互的事件.常见的触屏事件包括点击(tap).双击(double tap).长按(long press).滑动(swipe).拖动(drag)等.触屏事件通常 ...
- 对C语言符号的一些冷门知识运用的剖析和总结
符号 目录 符号 注释 奇怪的注释 C风格的注释无法嵌套 一些特殊的注释 注释的规则建议 反斜杠'\' 反斜杠有续行的作用,但要注意续行后不能添加空格 回车也能起到换行的作用,那续行符的意义在哪? 反 ...