C++ 深入解析new关键字,::new、operator new函数,placement new表达式

1. new运算符和operator new()

new: 指我们在C++里通常用到的关键字。比如 A *a=new A,为了实现这一目的,应用程序需要重载new运算符和delete运算符以控制内存分配的过程。对于new来说,有new和::new之分,前者位于std命名空间中

operator new(或operator new[]):是一个标准库函数,并不是运算符。对于operator new 来说,分为全局重载类重载,全局重载是void* ::operator new(size_t size),在类中重载形式void* A::operator new(size_t size)。还要注意的是这里的operator new()完成的操作一般只是分配内存,事实上系统默认的全局重载也只是调用malloc分配内存,并且返回一个void* 指针。

2.new 和 operator new之间的联系

(1)A* a=new A ; 这行代码实际执行三步操作

           1)new表达式调用一个名为operator new(或者operator new[])的标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储A类型的对象(或者对象数组);

           2)编译器运行相应地构造函数以构造这些对象,并为其传入初始值;

           3)对象被分配了空间并构造完成,返回一个指向该对象的指针

      事实上,如果类A重载了operator new,那么将调用A::operator new(size_t size),如果没有重载,就调用全局函数::operator new(size_t size),全局new操作符由c++默认提供。

(2)operator new接口和operator delete接口

        标准库定义了以下8个版本。其中前4个版本可能抛出bad_alloc异常,后4个版本不会抛出异常;

                  //这些版本可能抛出异常

                  void* operator new (size_t);

                  void*  operator new[] (size_t);

                  void* operator delete (void*) noexcept;

                  void* operator delete[] (void*) noexcept;

                  void* operator new (size_t, nothrow_t&)  noexcept;

                  void*  operator new[] (size_t, nothrow_t&) noexcept;

                  void* operator delete (void*, nothrow_t&) noexcept;

                  void* operator delete[] (void*,nothrow_t&) noexcept;

        对于operator new函数:要实现不同的内存分配行为,应该重载operator new,而不是new。

        operator new就像operator + 一样,是可以重载的。如果类中没有重载operator new,那么调用的就是全局的::operator new 来完成堆的内存申请。同理,operator new[]、operator delete、operator delete[]也是可以重载的。

        尽管operator new函数和operator delete函数一般用于new表达式,然而它们毕竟是标准库的两个普通函数,因此普通的代码也可以直接调用它们

3. ::new与new

       默认情况下编译器会将new这个关键字翻译成全局::operator new和相应的构造函数。

       但在有的情况下,用户自己会在类中重载operator new。这种情况下,编译器默认会使用类中重载的operator new(本质上因为编译器会从命名空间由内向外查找自己想要的函数,选用第一个)。

        如果我们想要继续使用默认的operator new,就应该写成::new ,字面意思是调用最外层命名空间中的operator new。

        值得一提的是,全局operator new也是可以被重载的。通过这种方式,我们可以改变所有new的部分行为。

4. placemen new 定位new表达式

 功能

        定位new允许我们在一个特定的、预先分配的内存地址上构造对象,实参不局限于动态内存

 形式

                  new  ( place_address )  type

                  new  ( place_address )  type (initializers)

                  new  ( place_address )  type [size]

                  new  ( place_address )  type [size]  { braced initializer list }

         其中place_address必须是一个指针,同时在initializers中提供了一个(可能为空的)以逗号分隔的初始值列表,该初始值列表将用于构造新分配的对象。

1)当仅通过一个地址值调用时,定位new使用operator new (size_t, void*)“分配”它的内存。这是一个我们无法重载的operator new版本。该函数不分配任何内存,它只是简单地返回指针实参;然后由new表达式负责在指定的地址初始化对象以完成整个工作。

        eg.     char*  ptr = new  char[ sizeof(T) ];             //allocate memory

                  T*  tptr = new(ptr) T;                                  //construct in allocated storage ("place")

                  tptr->~T();                                                 //destruct

                  delete[]  ptr;                                              //deallocate memory

2) 如果不是1)情况,则表达式返回place_address地址。

      (C++ Primer 第五版 P753)

        eg.   Token &Token::operator=( const std::string &s)

                 {

                      if ( tok ==STR)                         //如果当前存储的是string,可以直接赋值

                            sval = s;

                      else

                            new(&sval) string(s);         //否则需要先构造一个string

                      tok = STR;                              //更新判别式

                      return *this;

                  }

        可以看出,通过定位new表达式,我们可以实现保存一块内存,反复构造析构,这样可以省略中间的多次分配内存。由于malloc内存会导致系统调用,这可以节省大量的系统开销

猜你喜欢

转载自blog.csdn.net/aishuirenjia/article/details/102979457
今日推荐