c++ new运算符是如何调用构造函数的

内存申请和对象构造

       
本文内容简短,只为记录下一次思考过程,事情起源于一句话,“new操作符会调用operator new分配内存再调用构造函数构造对象”,但最近再次看到这句话的时候越看越有疑问,怎么样调用构造函数??于是就带着这个问题顺便看下operator new和placement new的源码加深下印象了。

众所周知构造函数是在实例化一个类对象时调用,通常我们看到的构造函数是下面这样用:

class A{};

int main()
{
    A inst = A();
    A inst2;
    A *p = new A();
}

       
上述几种调用方式,不管是在栈上还是堆上实例化一个类对象时,共同点都是内存的申请和类的构造是绑定在一起的。可以试想一下,既然new是先调用operator new再调用构造函数,那么这个过程如果我们自己手动调用呢:

#include <iostream>
using namespace std;

class A{
public:
    A():a(0){cout<<"A() called!"<<endl;}
    int a;
};

int main()
{
    A *p = (A*)::operator new(sizeof(A));// 直接调用operator new,只分配内存
    p->A();
}

看起来是没什么问题,但实际结果呢?直接编译出错,报错为:

错误:错误地使用了‘A::A’

即使更改为显示调用,仍然无法编译通过:

int main()
{
    A *p = (A*)::operator new(sizeof(A));// 直接调用operator new,只分配内存
    p->A::A();
}

错误:不能直接调用构造函数‘A::A’

到这里就产生了深深的疑问,构造函数不能被显示调用,那new是怎么调用构造函数的?

placement new

另一方面,我们知道placement new就是在已分配内存上构造对象的,那么placement new是如何只调用构造函数的?

class A{
public:
    A():a(0){cout<<"A() called!"<<endl;}
    int a;
};

int main()
{
    char buff[4];
    A *p = new(buff) A;// 在buff上调用构造函数
    p->~A();// placement new需要显式析构
    return 0;
}

答案很简单

先来看下operator new和placement new的实现:

  • operator new:
_GLIBCXX_WEAK_DEFINITION void *
    operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
    {
      void *p;

      /* malloc (0) is unpredictable; avoid it.  */
      if (sz == 0)
        sz = 1;
      p = (void *) malloc (sz);
      while (p == 0)
        {
          new_handler handler = std::get_new_handler ();
          if (! handler)
            _GLIBCXX_THROW_OR_ABORT(bad_alloc());
          handler ();
          p = (void *) malloc (sz);
        }

      return p;
    }
  • placement new:
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT{ return __p; }
// Default placement versions of operator delete.
inline void operator delete  (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { } 

       
可以看到,operator new只是对malloc的一些封装,而placement new则是什么都没做只是把指针返回。至此也基本可以猜到答案了,答案就是编译器自动调用的,想一下也必然是这样,new是个运算符不是函数,运算符是一种告诉编译器执行特定的数学或者逻辑操作的符号,又何来定义之说。

下面是一些参考:
Thus, for a class type T, a new-expression such as:

pt = new T;

translates more-or-less into something like:

pt = static_cast(operator new(sizeof(T)));
pt->T();

The first statement acquires storage for a T object by calling operator new, and converts the address of that storage from type void * to type T *. The second initializes the storage by applying T’s default constructor. As I mentioned earlier, a C++ compiler won’t let you write this explicit constructor call, but it’s happy to do it for you.

参考链接

[1] Calling Constructors with Placement New -Dr.Dobb’s
[2] When is the constructor called by ‘new’ operator in C++
[3] 源码
[4] 源码

  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值