1. new操作符

new运算符用于在堆上动态分配

如果分配成功,则返回指向分配内存的指针;如果分配失败,则抛出std::bad_alloc异常

new内置类型

// 动态申请一个int类型的空间
int* a = new int;
// 动态申请一个int类型的空间并初始化为11
int* b = new int(11);

// 动态申请3个int类型的空间
int* c = new int[3];
// 动态申请10个int类型的空间,并进行部分初始化
int* d = new int[10]{ 1,2,3 };

new自定义类型

执行过程:

  1. 调用operator new函数分配内存。
  2. 调用类的对应构造函数生成对象。
  3. 返回对应指针。
Foo* f1 = new Foo;
Foo* f2 = new Foo();

Foo* f3 = new Foo[3];
Foo* f4 = new Foo[3]{Foo(), Foo(), Foo()};

new和malloc的区别

  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换;而malloc内存分配成功则是返回void * ,需要强制类型转换
  • new操作符内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;而malloc分配内存失败时返回NULL。
  • new操作符申请内存分配时无须指定内存块的字节大小,编译器会根据类型信息自行计算;而malloc则需要显式地指出所需内存的字节大小。
  • new会调用对象的构造函数以完成对象的构造;而malloc则不会。

2. operator new

operator new是系统提供的全局函数,在new头文件中定义;

operator new执行的操作只是分配内存,事实上全局函数::operator new只是调用malloc分配内存,并且返回一个void*指针。

而new运算符执行的操作是:1. 调用operator new 分配内存;2. 调用构造函数生成对象

throw new

在分配失败的情况下,抛出异常std::bad_alloc,而不是返回NULL

void* operator new  ( std::size_t ) (std::bad_alloc);
void* operator new[]( std::size_t ) (std::bad_alloc);

void* operator new  ( std::size_t, std::align_val_t ) (std::bad_alloc);
void* operator new[]( std::size_t, std::align_val_t ) (std::bad_alloc);

nothrow new

nothrow new在分配失败的情况下,不抛出异常,而是返回NULL

void* operator new  ( std::size_t, const std::nothrow_t& );
void* operator new[]( std::size_t, const std::nothrow_t& );

void* operator new  ( std::size_t, std::align_val_t, const std::nothrow_t& ) noexcept;
void* operator new[]( std::size_t, std::align_val_t, const std::nothrow_t& ) noexcept;

编译器会自动计算所需分配的内存大小,并传递给 operator new。所以不需要显式地传递size参数。

operator new的重载

如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。 重载operator new时:

  • 返回类型必须声明为void*
  • 第一个参数类型必须为表达要求分配空间的大小,类型为size_t
  • 可以带其它参数
void* operator new  ( std::size_t count, /* args... */ );
void* operator new[]( std::size_t count, /* args... */ );

void* operator new  ( std::size_t count, std::align_val_t al, /* args... */ );
void* operator new[]( std::size_t count, std::align_val_t al, /* args... */ );

如何限制对象只能建立在堆上或者栈上:

  • 只能建立在堆上:设置析构函数为protected
class A {
protected:
    A(){}
    ~A(){}
public:
    static A* create()
    {
        return new A();
    }
    void destory()
    {
        delete this;
    }
};
  • 只能建立在栈上:重载operator new并设为private
class A {
private:
    void* operator new(size_t t){}
    void operator delete(void* ptr){}
public:
    A(){}
    ~A(){}
};

3. placement new

void* operator new  ( std::size_t count, void* ptr );
void* operator new[]( std::size_t count, void* ptr );

placement new只做对象构造,而不申请内存。它可以在预先分配好的内存上构造对象。

placement new本质上是对operator new的重载,在new头文件中定义。它不分配内存,而是调用对应构造函数在ptr所指的内存中构造一个对象,之后返回ptr

placement new不可重载。

int main()
{
    char mem[100];
    Foo* f = new (mem)Foo;
}