`

new, operator new 和 placement new

 
阅读更多

一、new 和 delete 的过程:

在进行一切讲解之前,首先,要了解两点:

第一, new 和delete都是C++中的关键字

第二, new不能被重载,它的行为总是一致的(delete相同,顺序相反):
            (1) 先调用operator new分配内存

            (2) 在调用构造函数初始化那段内存中的对象

            (3) 返回相应指针

 

二、new , operator new 和 placement new的关系

 

1. new : 不能被重载,它的行为总是一致的(delete相同,顺序相反):
            (1) 先调用operator new分配内存

            (2) 在调用构造函数初始化那段内存

 

2. operator new : 如同operator + 一样,可以被重载。如果类中没有重载operator new, 那么调用全局的 ::operator new 来完成堆的分配。同理 operator new[], operator delete, operator delete[]也可以重载。

 

3. placement new : 只是operator new的一个重载版本。作用是——在预先定义的内存位置构造一个对象。下面详解。

 

三、placement new详解

    下面这篇文章详解了placement new的使用 – by赵湘宁。

    常常有人问这样一个C++问题:如何在预先定义的内存位置构造一个对象?在预先定义的内存缓冲构造一个对象有许多有用的应用。例如,一个定制的垃圾搜集器能使用一个大的预分配内存缓冲,用户在这个缓冲中构造其对象。当不再需要这些对象时,它们的存储空间被自动收回。

   这个技术在重视时间的应用中也很有用。在预先分配的内存缓冲构造一个对象是一种“时间常量”操作,之所以这样说是因为程序分配操作本身不会浪费宝贵的时间。同时也要注意当系统没有足够的内存时,动态内存分配可能失败。因此,对于重视任务的应用,预先分配一个足够大的缓冲有时是不可避免的。

    许多应用需要在给定的时间构造不同类型的对象。想一想这样一个例子,一个GUI应用根据用户的输入,每次、显示不同的对话框,利用重复分配和释放内存,这个应用能提前创建一个内存缓冲,并能在这个缓冲里反复构造和销毁不同类型的对象。

    C++提供了几种特点来方便实现在预先决定的内存位置构造一个对象的任务。在这些特点中,包括一个特殊形式的new操作符,叫做“定位new”(placement new)操作,以及一个显式的析构处理。实现方法如下:

    第一步:分配一个足够的内存缓冲区,以便存放给定类型的对象。如果想要每次构造不同类型的对象,需要至少以最大的对象所占空间的大小分配一个缓冲。预分配的缓冲是在可用内存空间中分配的纯字符数组。

char * buff = new char [sizeof (Foo) ];  

    一旦分配了缓冲,就能在缓冲中构造每一种类型的对象。为此,使用特殊版本的new操作符(“定位new”),以缓冲地址为placement new的参数。为了使用placement new,必须包含标准头文件<new>。下面的代码片断中,使用placement new操作在内存地址buff上构造类型为Foo的对象。

#include <new>
Foo * pfoo = new (buff) Foo; //使用new操作在buff上构造一个 Foo   

    Placement new 以先前分配的缓冲(buff)地址作为参数,并在这个缓冲上构造给定类型的对象。他返回构造对象的指针,这个对象指针的使用与通常的指针使用没什么两样。

        unsigned int length = pfoo->size();

        pfoo->resize(100, 200);

        length = pfoo->size();  

    当不再需要这个对象的时候,必须显式调用其析构函数释放空间。做这件事是有一些技巧的,因为许多人错误地假设对象会被自动销毁,错也!。在预分配的缓冲里构造另一个对象之前或者在释放缓冲之前,如果忘了显式调用析构函数,程序将产生不可预料的后果。显式的析构器声明如下:

pfoo->~Foo(); //显式调用析构函数  

    换句话说,一个显式的析构器与普通的成员函数调用一样,只是名字与普通的成员函数稍有差别。一旦对象被销毁,便可以在预分配的内存中再次构造另一个对象。实际上,这个过程可以无限制地重复:构造一个对象,销毁它,然后又反复利用预分配的缓冲构造新对象。

    当不再需要预定义的缓冲时,或者说当应用程序关闭时,必须释放预定义的缓冲。使用delete[]完成这个任务,因为预定义的缓冲是一个字符数组。下列代码包含一个完整的例子的所有步骤,包括最终缓冲的释放:

#include <new>

void placement_demo(){ 
    //1. 预分配缓冲
    char * buff = new char [sizeof (Foo) ];  

    //2. 使用 placement new
    Foo * pfoo = new (buff) Foo;  

    //使用对象
   unsigned int length = pfoo->size();  
    pfoo->resize(100, 200);

    //3. 显式调用析构函数
    pfoo->~Foo();  

    //4. 释放预定义的缓冲
    delete [] buff;  
} 

 

例二:

class CTest {
     /* 成员函数和成员数据 */
};

// 分配一个对象
CTest * pTest = new Test;

// 分配一个有十个对象的数组 (CTest 要有缺省构造函数default constuctor)
CTest * p10Tests = new Test[ 10]; 

 

虽然这种写法在大多数时候都工作得很好,但还是有些情况下使用new是很烦人的,比如当你想重新分配一个数组或者当你想在预分配的内存上构造一个对象的时候。

 

 

 比如第一种情况,重新分配一个数组效率是很低的:

// 分配一个有10个对象的数组
CTest * pTests = new Test[ 10];


// 假设现在我们需要11个对象
CTest * pNewTests = new Test[ 11];

// . . . 我们必须把原来的对象拷贝到新分配的内存中
for ( int i = 0; i < 10; i++)
    pNewTests[ i] = pTests[ i];

delete pTests;

pTests = pNewTests; 

   如果你想在预分配的内存上创建对象,用缺省的new操作符是行不通的。要解决这个问题,你可以用placement new构造。它允许你构造一个新对象到预分配的内存上:

 

// buffer 是一个void指针 (void *)

// 用方括号[] 括起来的部分是可选的

[CYourClass * pValue = ] new( buffer) CYourClass[( parameters)];

 

下面是一些例子:

#include <new>

class CTest {
public:
    CTest() {}
    CTest(int) {}
};

int main(int argc, char* argv[]) {

    // 由于这个例子的目的,我们不考虑内存对齐问题
   char strBuff[ sizeof(CTest) * 10 + 100];

    CTest * pBuffer = (CTest *)strBuff;

    // 缺省构造
    CTest * pFirst = new (pBuffer) CTest;

    // 缺省构造
    CTest * pSecond = new (pBuffer + 1) CTest;

    // 带参数的构造;
   // 不理会返回的指针
   new (pBuffer + 2) CTest(5);
 
    // 带参数的构造
   CTest * pFourth = new (pBuffer + 3) CTest(10);

    // 缺省构造
   CTest * pFifth = new (pBuffer + 4) CTest();

    // 构造多个元素(缺省构造)
   CTest * pMultipleElements = new (pBuffer + 5) CTest[5];

    return 0;
} 

  

 

当你有自己的内存缓冲区或者在你实现自己的内存分配策略的时候,placement new会很有用。事实上在STL中广泛使用了placement new来给容器分配内存;每个容器类都有一个模版参数说明了构造/析构对象时所用的分配器(allocator)。

 

 

在使用placement new的时候,你要记住以下几点:

 

加上头文件#include <new>

 

你可以用placement new构造一个数组中的元素。

 

要析构一个用placement new分配的对象,你应该手工调用析构函数(并不存在一个“placement delete”)。它的语法如下:

pFirst->~CTest();

pSecond->~CTest();

 

 

参考文档

三者关系: http://www.cnblogs.com/wanghetao/archive/2011/11/21/2257403.html

placement new 详解: http://blog.csdn.net/michaelgs/article/details/862971

分享到:
评论

相关推荐

    new操作符(new operator)和operator new的区别

    在C++编程中,`new`操作符和`operator new`是两个密切相关但不完全相同的概念,它们在内存管理和对象创建方面各自承担着特定的角色。 `new`操作符是C++语言内置的关键字,用于在堆上动态分配内存并创建对象。当执行...

    placement new详解

    - 提高**时间和空间效率**:placement new避免了在运行时进行内存搜索和分配,减少了内存碎片和内存不足的风险,特别适合于实时系统或长时间运行的程序。 **使用步骤**: 1. **缓存的提前分配**:可以使用`new`在...

    全面解析C++中的new,operator new与placement new

    new operator/delete operator就是new和delete操作符,而operator new/operator delete是函数。 new operator(1)调用operator new分配足够的空间,并调用相关对象的构造函数(2)不可以被重载 operator new(1)只...

    c语言中new_C++的new归纳详解

    本文详细介绍了 C++ 中的 new,包括 new operator、operator new、placement new 三种形态的内容,并且详细解释了每一种形态的行为和用法,同时也提到了 delete 的相关知识点,希望对读者有所帮助。

    C++_new_operator详解

    `new`操作符在内部会调用两个重要的函数:`operator new` 和 `placement new`。 #### 二、new操作符的三种形式 1. **new操作符**:这是最常用的形式,用于动态创建一个对象或数组。 2. **operator new**:这是一个...

    关于new和delete的详细用法

    关于new和delete的详细用法 1. new是C++的一个关键字,同时也是操作符。它可以用于在堆上动态创建一个对象,实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。 2. new的三种形态:new operator...

    深入C++的new关键字

    new可以分为三种形态:new operator、operator new、placement new。new operator是我们平时所使用的new,其行为就是前面所说的三个步骤。operator new是可以重载的,用于分配内存的操作符。placement new是用来实现...

    c语言newC++的new.pdf

    在本文中,我们将深入探讨new操作符的三种形态,即new operator、operator new和placement new。 new operator是我们平时所使用的new操作符,它的行为可以分为三步:获得一块内存空间、调用构造函数、返回正确的...

    c++中new的三种用法详细解析

    在C++编程语言中,`new`运算符是动态内存管理的关键部分,用于在运行时分配和初始化对象。以下是`new`的三种主要用法的详细解析: 1. **Plain new**: 这是最常见的`new`使用方式,它负责分配内存并调用构造函数来...

    总结C++中三种关于new的使用方法.docx

    new 在 C++ 中有三种使用方法:new 表达式、operator new 和 placement new。每种方法都有其特点和应用场景,程序员需要根据实际情况选择合适的方法来使用 new。同时,delete 语句也需要根据实际情况选择合适的方法...

    c语言newC++的new[收集].pdf

    5. **`operator new` 和 `operator delete` 重载** - 如果重载了 `operator new`,为了保持一致性,通常也需要重载 `operator delete`,以便在释放内存时执行自定义操作。 6. **`placement new` 示例** - 使用 `...

    C++new运算符[参考].pdf

    C++ new 运算符的含义可以分为三种:new 运算符、new 函数和 placement new。每种含义都有其特点和用法。 new 运算符 new 运算符是 C++ 中最常用的 new,它作为运算符,用于在堆上分配一块内存,并自动调用类的...

    重载new 用法例子

    1. **重载的语法**:在C++中,你可以通过在类的作用域或全局作用域内定义`operator new`和`operator delete`函数来实现`new`的重载。例如: ```cpp void* operator new(size_t size) { // 自定义内存分配逻辑 } ...

    深入C++基础_new运算符.doc

    `new`操作符在C++中扮演着重要角色,它不仅提供了动态内存分配的能力,还允许开发者通过重载`operator new`和`placement new`等方式来定制内存管理和对象构造的过程。掌握这些细节有助于编写更加高效和安全的C++程序...

    总结C++中三种关于new的使用方法.pdf

    在C++编程语言中,`new`关键字有三种主要的使用方法,这些方法可以被归类为两大类:new表达式和new操作符。每种用法都有其特定的用途和场景,对于理解和掌握C++内存管理至关重要。 首先,我们来看最常用的**new...

    cpp代码-只能创建栈对象 做法:将operator new/delete设置为私有的

    本篇我们将深入探讨如何通过将`operator new`和`operator delete`设置为私有来实现这一目标,以及这种做法的潜在影响和应用场景。 首先,`operator new`和`operator delete`是C++中的全局运算符,用于动态内存分配...

    浅析C++ new的三种面貌

    然而,许多人对 new 的理解仅止于其基本用法,殊不知 new 还有三种不同的面貌:new operator、operator new() 和 placement new()。本文将深入探讨 C++ 中 new 的三个面貌,帮助大家更好地理解和学习 C++。 1. new ...

    分配内存方法

    在C++编程语言中,`new`关键字...通过重载`operator new`和`placement new`,开发者可以定制内存管理行为,以适应特定的应用场景。在编写C++代码时,正确使用`new`及其相关操作符是保证程序健壮性和性能的重要方面。

Global site tag (gtag.js) - Google Analytics