new

new 是C++ 中的关键字,有两个含义

  1. new 表达式
  2. 作为运算符的函数名,也就是 operator new

new 表达式

提供一个特定的内存分配格式,返回在存储空间上构造的对象或对象数组的一个 纯右值 指针。

语法

  • :: (可选) new (布置参数)(可选) (类型) 初始化器(可选)
  • :: (可选) new (布置参数)(可选) 类型 初始化器(可选)

说明

  1. 布置参数为分配的内存
  2. 初始化器则有两种,分别是(){} 初始化方式,可以包括 auto decltype(auto) 等占位类型说明符
char* ptr = new char[sizeof(T)]; // 分配内存
T* tptr = new(ptr) T;            // 在已分配存储中构造(“布置”)
tptr->~T();                      // 销毁
delete[] ptr;                    // 解分配内存

一些注意点

  1. 表达式中的 类型为贪心,比如 new int * 1 实际上是 int 先结合 *,(这个会编译失败)。
  2. 在对象数组分配内存时,一维之外的维数必须指定为整数常量。
  3. new 表达式是为对象分配是调用 operator new 来完成,对象的大小可以在编译期得到 (开优化),之后进行对象的构造,构造过程的规则无特殊之处。

operator new

new 表达式通过调用 operator new 来分配内存,分配对象数组为 operator new[].

C++ 自身提供了全局的函数(以::开始),和用户自定义的替换函数,如果 new T; 中T为类类型,则从T的类作用域中开始查找替换函数。

基本的函数格式如下:

void* T::operator new  ( std::size_t count );

这里我们自定义一个例子,operator new 是我们自定义的分配函数,后面部分是对在已经分配的内存上进行再分配的情况

  • 在已有内存上分配空间,不会再调用分配函数,
  • 每次分配的内存地址都相同。
    所以每次调用默认布置分配函数的时候,只是一个内存控制权的转移,这段内存的生命生命周期应该在其上构造对象之后。
#include <iostream>
#include <cstdlib>
#include <cstdio>

void *operator new(size_t n) {
    printf("operator new: %zu\n", n);
    return std::malloc(n);
}
struct Foo {
    uint64_t valu64;
    int64_t vali64;
    char ch;

    Foo() : valu64(1), vali64(3), ch('c') {}
};

int main() {
    char *ptr = new char[16]; // std::malloc(16);
    Foo *tptr = new (ptr) Foo; // 在已分配存储中构造(“布置”), 这里有溢出风险
    tptr->~Foo(); // 销毁
    delete[] ptr; // 解分配内存

    Foo *fp = new Foo; // std::malloc(24);

    char arr[36];
    printf("init arr addr: %p\n", arr); // 0x7ffe8f9748e0
    Foo *fptr = new (arr) Foo;
    printf("Foo addr: %p\n", fptr); // 0x7ffe8f9748e0
    char *ch = new (arr) char;
    printf("ch addr: %p\n", ch); // 0x7ffe8f9748e0, 三者的地址是一样的
    fptr->~Foo();                // 销毁
}

参考

  1. new. C++ 关键字 new.

// 过年疫情导致不能出门,在家里学习学习,记点笔记。