new、placement new、delete、placement delete

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/finewind/article/details/82631906

new表达式

c++提供了new关键字和delete关键字,分别用于申请和释放内存空间,其中new表达式的语法如下:

new new-type-id (optional-initializer-expression-list)

new表达式做两件事情:

  • 在堆(heap)上申请一块空间,大小等于sizeof(new-type-id)
  • 在申请的空间上构建对象,即调用对象的构造函数

new还提供了一种表达式(即placement new表达式),用于在自定义内存空间上构造对象,语法如下:

new (expression-list) new-type-id (optional-initializer-expression-list)
// 示例:
void *buffer = malloc(sizeof(ClassA));
ClassA *ptr = new(buffer) ClassA();

int i;
int *pi = new(&i) int(10);

这种表达式只完成了步骤2,即只调用了对象的构造函数,而把步骤1留给用户实现。

delete表达式

c++提供的delete表达式语法:

delete type_pointer;

delete表达式完成两件事:

  • 调用对象的析构函数
  • 释放对象所在的内存空间,返回给系统

    若type_pointer等于NULL,delete表达式能正常工作,但对同一给指针重复调用delete将带来未定义错误。
    delete表达式没有placement版本。

标准operator new和operatoer delete

c++标准的new expression内部调用的是标准的operator new(),它的定义如下:

void *operator new(std::size_t) throw(std::bad_alloc);

operator new()是一个操作符或函数,它完成标准的allocation,即内存分配。

c++标准的delete expression内部调用的是标准的operator delete(),它的定义如下:

扫描二维码关注公众号,回复: 3292711 查看本文章
void operator delete(void*) throw();

operator delete()也是一个操作符或者函数,它完成过标准的deallocation,即内存释放。

placement new和placement delete

C++标准的 new 表达式能完成大部分的需求, 但不是全部, 比如: 如何在已有的内存空间上创建对象, 标准 new 表达式做不了, C++也不支持直接在raw内存上调用对象的构造函数, 于是placement new就产生了, 取名placement, 也说明了它的历史来源, 即在原地构建一个新对象.
placement new expression和placement operator new()通常被笼统的placement new。但两者概念不同:
placement operator new()是标准operator new()的一个重载函数。
placement new expression,它属于c++语法,就像标准new expression调用的是标准operator new()一样,placement new expression调用的是placement版本的operator new()。
placement delete表示placement operator delete()函数,因为根本不存在placement delete expression。
既然没有placement delete expression, 那为啥还需要placement operator delete(), 一个重要的原因是, C++需要placement operator delete()和placement operator new()成双成对. 假设一种情况:

当你调用placement new expression构建对象, 结果在构造函数中抛出异常, 这个时候怎么办, C++ 只能调用相应的placement operator delete(), 释放由placement operator new()获取的内存资源, 否则就会有内存泄露. 通过下面的例子可以感受一下:

#include <cstdlib>
#include <iostream>

struct A {} ;
struct E {} ;

class T {
public:
    T() { throw E() ; }
} ;

void * operator new ( std::size_t, const A & )
    {std::cout << "Placement new called." << std::endl;}
void operator delete ( void *, const A & )
    {std::cout << "Placement delete called." << std::endl;}

int main ()
{
    A a ;
    try {
        T * p = new (a) T ;
    } catch (E exp) {std::cout << "Exception caught." << std::endl;}
    return 0 ;
}

placement operator new()和placement operator delete()

placement operator new()、placement operator delete()和标准operator new()、operator delete()的区别是添加了自定义参数:placement operator new()的第一个函数参数必须是std::size_t,表示要申请的内存的大小;placement delete(),它的第一个函数参数必须是void*, 表示要释放的对象指针:

void * operator new(std::size_t) throw(std::bad_alloc); // 标准版本
void * operator new(std::size_t, const std::nothrow_t &) throw();  // placement版本
void operator delete(void*) throw();  // 标准版本
void operator delete(void*, const std::nothrow_t &) throw(); // placement版本

注意: nothrow版本的new,也是c++标准,它就是通过placement new和placement delete实现的。相应的nothrow版本的new expression是这样的:

T *t = new(std::nothrow) T;

在用户自定义空间上构建对象,是placement new的本意,它被做到c++标准中,作为default placement:

void * operator new(std::size_t, void*p) throw() { return p; }
void operator delete(void*, void*) throw() {}

相应的placement new expression使用起来是这样的:

void *buffer = malloc(sizeof(ClassA));
ClassA *ptr = new(buffer) ClassA();

mempool用到的placement new

实际应用中,内存池用到最多。如以下示例:

// 1. 内存池定义:
class Arena {
public:
    void* allocate(size_t);
    void deallocate(void*);
    // ...
};

// 2. 定义placement new和placement delete作用于内存池:
void* operator new(size_t sz, Arena& a)
{
    return a.allocate(sz);
}
void operator delete(void *ptr, Arena& a)
{
    return a.deallocate(ptr);
}
// 3. 创建对象:
Arena a;
X *p = new(a) X;

// 4. 删除对象:
// 不能调用delete p;因为这样只能启用标准operator delete(),而不是Arena的placement版本
// 法1:
p->~X();
operator delete(p, a);
// 法2: 将两个操作封装成一个template func
template<class T> void destroy(T* p, Arena& a)
{
    if (p) {
         p->~T();       // explicit destructor call
         a.deallocate(p);
    }
}

destroy(p, a);

转自:

https://www.jianshu.com/p/4af119c44086

猜你喜欢

转载自blog.csdn.net/finewind/article/details/82631906