C++:动态内存分配

12 篇文章 0 订阅
12 篇文章 0 订阅


承接C++:生存期、作用域和链接性


1. 动态内存

使用运算符new分配的内存称为动态内存(dynamic memory,所在内存块称为堆heap或者自由存储free store),其不受作用域和链接性规则的约束,所以可以在一个函数中分配而在另一个函数中回收/释放。程序终止时,new分配的内存一般被释放,但也不总是成立。所以最好的做法是使用运算符delete释放由new分配的内存,二者前后搭配使用。

2. new运算符和函数

newdeletenew[]delete[]要对应搭配使用。

分配运算符分配函数回收运算符回收函数
newvoid * operator new(std::size_t);deletevoid operator delete(void *);
new[]void * operator new[](std::size_t);delete[]void operator delete[](void *);

以下代码示例中,两种定义ab的方式等价:

int * a = new int;
int * a = (int *) operator new(sizeof(int));	// equivalent

int * b = new int[10];
int * b = (int *) operator new(10 * sizeof(int));	// equivalent

3. 初始化

除了动态分配内存,new运算符还可以初始化变量,有两种方式:

  1. 小括号()语法:适用于纯量(scalar)内置数据类型和有适当构造函数的类。
  2. 大括号{}列表初始化:适用于普通结构体或数组。
int * a = new int(1);
int * b = new int[3]{2, 3, 4};

4. 定位new运算符

new运算符有一个变体,叫做定位new运算符(placement new operator),允许指明特定存储位置(比如int * a = new (buffer) int;),而常规new运算符默认使用内存块堆heap。

代码示例1

#include <iostream>
#include <new>      // placement new

using namespace std;

const int MAXSIZE = 512;	// static global variable (internal linkage)
char buffer[MAXSIZE];       // global & static varibale

int main(int argc, char const *argv[]) {
    int n = 5;
    cout << "Calling new and placement new the first time:\n";
    double * ptr1 = new double[n];          // zero-initialization
    double * ptr2 = new (buffer) double[n]; // zero-initialization
    cout << "Memory addresses:\n";
    cout << "\theap: " << ptr1 << ", static: " << ptr2 << '\n';
    // for (int i = 0; i < n; i++) {
    //     cout << ptr1[i] << " at " << ptr1 + i << "; ";
    //     cout << ptr2[i] << " at " << ptr2 + i << '\n';
    // }   // all zeros
    for (int i = 0; i < n; i++) {
        ptr1[i] = ptr2[i] = 1000 + 20 * i;
    }
    cout << "Memory content:\n";
    for (int i = 0; i < n; i++) {
        cout << ptr1[i] << " at " << ptr1 + i << "; ";
        cout << ptr2[i] << " at " << ptr2 + i << '\n';
    }
    cout << '\n';

    cout << "Calling new and placement new the second time:\n";
    double * ptr3 = new double[n];  // allocate new memory
    // ptr4 same address as ptr2, overwrite old data
    double * ptr4 = new (buffer) double[n];
    for (int i = 0; i < n; i++) {
        ptr3[i] = ptr4[i] = 1000 + 40 * i;
    }
    cout << "Memory content:\n";
    for (int i = 0; i < n; i++) {
        cout << ptr3[i] << " at " << ptr3 + i << "; ";
        cout << ptr4[i] << " at " << ptr4 + i << '\n';
    }
    cout << '\n';

    cout << "Calling new and placement new the third time:\n";
    delete[] ptr1;          // free ptr1
    ptr1 = new double[n];   // reuse memory
    ptr2 = new (buffer + n * sizeof(double)) double[n]; // address offset
    for (int i = 0; i < n; i++) {
        ptr1[i] = ptr2[i] = 1000 + 60 * i;
    }
    cout << "Memory content:\n";
    for (int i = 0; i < n; i++) {
        cout << ptr1[i] << " at " << ptr1 + i << "; ";
        cout << ptr2[i] << " at " << ptr2 + i << '\n';
    }
    
    return 0;
}

输出

Calling new and placement new the first time:
Memory addresses:
    heap: 0x7fd483c05950, static: 0x10445e0d0
Memory content:
1000 at 0x7fd483c05950; 1000 at 0x10445e0d0
1020 at 0x7fd483c05958; 1020 at 0x10445e0d8
1040 at 0x7fd483c05960; 1040 at 0x10445e0e0
1060 at 0x7fd483c05968; 1060 at 0x10445e0e8
1080 at 0x7fd483c05970; 1080 at 0x10445e0f0

Calling new and placement new the second time:
Memory content:
1000 at 0x7fd483c05a20; 1000 at 0x10445e0d0
1040 at 0x7fd483c05a28; 1040 at 0x10445e0d8
1080 at 0x7fd483c05a30; 1080 at 0x10445e0e0
1120 at 0x7fd483c05a38; 1120 at 0x10445e0e8
1160 at 0x7fd483c05a40; 1160 at 0x10445e0f0

Calling new and placement new the third time:
Memory content:
1000 at 0x7fd483c05950; 1000 at 0x10445e0f8
1060 at 0x7fd483c05958; 1060 at 0x10445e100
1120 at 0x7fd483c05960; 1120 at 0x10445e108
1180 at 0x7fd483c05968; 1180 at 0x10445e110
1240 at 0x7fd483c05970; 1240 at 0x10445e118

定位new运算符仅仅使用传入的地址分配内存,并不会记录相应内存是否已经被占用,也不会搜索未占用的内存块。所以代码示例中第二次调用定位new运算符时,由于ptr4ptr2使用的地址相同,ptr4将覆盖原来的数据。而第三次调用定位new运算符时,ptr2的传入地址偏移了ndouble型数据的大小。所以从打印出的数据地址可以看出,ptr2相应数组起始地址紧随ptr4相应数组末尾元素地址。

另外在上面的代码示例中,不能按照常规做法使用delete运算符释放定位new运算符所分配的内存,因为buffer所指内存是静态内存,而delete只是用于释放由常规new所分配的动态内存。


  1. 改编自C++ Primer Plus 6th Edition by Stephen Prata ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值