new/delete/array new/array delete/placement new/重载new delete

new

在这里插入图片描述
当使用new的时候,实际上调用的是operator new,operator new在<new>头文件中:

//@{
/** These are replaceable signatures:
 *  - normal single new and delete (no arguments, throw @c bad_alloc on error)
 *  - normal array new and delete (same)
 *  - @c nothrow single new and delete (take a @c nothrow argument, return
 *    @c NULL on error)
 *  - @c nothrow array new and delete (same)
 *
 *  Placement new and delete signatures (take a memory address argument,
 *  does nothing) may not be replaced by a user's program.
*/
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));

// 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 { }
//@}

new具体操作:

//头文件先忽略
using std::new_handler;
using std::bad_alloc;// using 声明
#if _GLIBCXX_HOSTED// 如果 有这个宏使用 std的malloc
using std::malloc;
#else// 没有则使用c语言的malloc
// A freestanding C runtime may not provide "malloc" -- but there is no
// other reasonable way to implement "operator new".
extern "C" void *malloc (std::size_t);
#endif

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
  void *p;
  // new_handler 以后说明,但是可以看出首先我们根据入口参数 sz的大小分配内存, 
  // 如果sz为0 则令其为1 ,然后在while循环中调用malloc申请内存
  // 直到 申请成功 或者 抛出异常或者 abort
  /* malloc (0) is unpredictable; avoid it.  */
  if (sz == 0)
    sz = 1;

  while (__builtin_expect ((p = malloc (sz)) == 0, false))
    {
      new_handler handler = std::get_new_handler ();
      if (! handler)
    _GLIBCXX_THROW_OR_ABORT(bad_alloc());
      handler ();
    }

  return p;
}

其实在new的实现中都是使用一个while循环去一直malloc数据,如果malloc失败的话,则调用_callnewh或者get_new_handler。侯捷说_callnewh是一种你可以设定的函数,那么应该在_callnewh中释放内存以便下次调用malloc的时候可以成功。

new出的指针与构造函数

使用new出的指针调用构造函数在某些编译器上是可以的,在某些编译器(gnu c)是不可以的。
但是使用new出的指针调用析构函数是可以的,只不过在调用delete的时候会再次调用析构函数。

#include <iostream>
using namespace std;
class A {
public:
  A() { cout << "ctor" << endl; };
  ~A() { cout << "dtor" << endl; };
private:
  int a;
};
int main()
{
  A* p = new A();
  // p->A();
  p->~A();
  delete p;
  return 0;
}

输出为:
在这里插入图片描述

delete

delete内部直接使用free.

array new/array delete

在这里插入图片描述
在delete new出来的数组的时候,如果错误使用了delete p而不是使用delete [] p;可能会引起内存泄漏,对没有指针变量的对象可能没有影响,对于有指针变量的对象就会产生内存泄漏。

#include <iostream>
using namespace std;
class A {
public:
  int id;
  A() { cout << "default ctor.this=" << this << " id=" << id << endl; };
  A(int _id) : id(_id) { cout << "ctor.this=" << this << " id=" << id << endl; };
  ~A() { cout << "dtor.this=" << this << " id=" << id << endl; };

};
int main()
{
  int size = 3;
  A* buf = new A[size];//調用3次構造函數, 0先于1先于2
  A* tmp = buf;
  cout << "buf=" << buf << " tmp=" << tmp << endl;

  for(int i = 0; i < size; i++)
    new(tmp++)A(i);//placement new
  cout << "buf=" << buf << " tmp=" << tmp << endl;

  delete[] buf;//dtor調用3次, 先析構2再析構1最後析構0

  return 0;
}

输出:

default ctor.this=0x866d84 id=8782016
default ctor.this=0x866d88 id=0
default ctor.this=0x866d8c id=0
buf=0x866d84 tmp=0x866d84
ctor.this=0x866d84 id=0
ctor.this=0x866d88 id=1
ctor.this=0x866d8c id=2
buf=0x866d84 tmp=0x866d90
dtor.this=0x866d8c id=2
dtor.this=0x866d88 id=1
dtor.this=0x866d84 id=0

可以看出,在析构的时候,析构顺序是从最后一个到第一个。

placement new

  • placement new允许在已经分配好的空间上构造对象。
  • 这个内存空间可以不是动态分配的。
  • placement new不能被重载。
  • 对于需要频繁new delete的情景,如果只是简单地调用new delete不仅会比较慢(寻找合适的内存块需要时间),还会造成内存碎片。我们可以事先分配好内存,每次在这片内存上使用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; }

第二个参数是一个指针,指向已经分配好的内存,然后直接在这片内存上通过new构造对象,它直接将传入的指针返回。
在这里插入图片描述
在这里插入图片描述
上图可以看出placement new被编译器解释为3步,首先调用operator new函数,然后强制类型转换成对象类型的指针,最后一步调用构造函数。

重载::operator new和::operator delete

全局的operator new以及operator delete是可以重载的,但一般不进行重载,因为这会影响到很多地方。

void* myAlloc(size_t size)
{ return malloc(size); }

void* myFree(void* ptr)
{ return free(ptr); }

inline void* operator new(size_t size)
{ 
	cout << "my operator new" << endl;
	return myAlloc(size);
}
inline void* operator new[](size_t size)
{ 
	cout << "my operator new[]" << endl;
	return myAlloc(size);
}
inline void* operator delete(void* ptr)
{ 
	cout << "my operator delete" << endl;
	return myFree(ptr);
}
inline void* operator delete[](void* ptr)
{ 
	cout << "my operator delete[]" << endl;
	return myFree(ptr);
}

在类中重载operator new与operator delete

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;

class Foo
{
public:
  int _id;
  // long _data;

public:
  Foo() : _id(0) {  
    cout << "default ctor.this=" << this << " id=" << _id << endl;
  };
  Foo(int i) : _id(i) {
   cout << "ctor.this=" << this << " id=" << _id << endl;
  };
  ~Foo() { cout << "dtor.this=" << this << " id=" << _id << endl; };
  
  //这里必须是static
  static void* operator new(size_t size);
  static void operator delete(void* pdead, size_t size);
  static void* operator new[](size_t size);
  static void operator delete[](void* pdead, size_t size);
};

void* Foo::operator new(size_t size){
  Foo* p = (Foo*)malloc(size);
  cout << "Foo operator new, size=" << size <<"\treturn " << p << endl;
  return p;
}

void Foo::operator delete(void* pdead, size_t size){
  cout << "Foo operator delete, pdead=" << pdead << " size=" << size << endl;
  free(pdead);
}

void* Foo::operator new[](size_t size){
  Foo* p = (Foo*)malloc(size);
  cout << "Foo operator new[], size=" << size <<"\treturn " << p << endl;
  return p;
}

void Foo::operator delete[](void* pdead, size_t size){
  cout << "Foo operator delete[], pdead=" << pdead << " size=" << size << endl;
  free(pdead);
}
int main()
{
  Foo* p = new Foo(7);
  delete p;

  Foo* pArray = new Foo[5];
  delete [] pArray;
  return 0;
}
注意在类中重载的operator new等函数必须是static,因为如果自己在类中重载了operator new,那么分配内存时就会调用自己重载的那个new。所以如果new不是static的,调用new的时候还没有对象产生,又该如何new出对象呢?将new声明为static就可以在没有对象的时候还能调用自己重载的operator new了,因为静态成员函数不需要对象生成就可以调用。当将运算符函数定义为类的成员时,他们是隐式静态的,无需显式地声明static。

上述代码的输出为:

Foo operator new, size=4	return 0xeca040
ctor.this=0xeca040 id=7
dtor.this=0xeca040 id=7
Foo operator delete, pdead=0xeca040 size=4
Foo operator new[], size=24	return 0xec0578
default ctor.this=0xec057c id=0
default ctor.this=0xec0580 id=0
default ctor.this=0xec0584 id=0
default ctor.this=0xec0588 id=0
default ctor.this=0xec058c id=0
dtor.this=0xec058c id=0
dtor.this=0xec0588 id=0
dtor.this=0xec0584 id=0
dtor.this=0xec0580 id=0
dtor.this=0xec057c id=0
Foo operator delete[], pdead=0xec0578 size=24

可以看出,如果自己在类中重载了operator new,那么会绕过全局的operator new,自动调用自己重载的operator new。当然也可以在new前面加上::表示使用全局的new而不是自己定义的new。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值