原文地址:http://www.cnblogs.com/jamesmile/archive/2010/04/17/1714311.html,在此感谢
C++中的operator new与new operator,看上去挺像的两姐妹,却有天壤之别。
重载的 operator new 必须是类成员函数或全局函数,而不可以是某一名空间
之内的函数或是全局静态函数。此外,还要多加注意的是,重载 operator new 时需要兼容默认的 operator new 的错误处理方式,并且要满足 C++ 的标准规定 :当要求的内存大小为 0byte 时也应该返回有效的内存地址。
如果使用不同的参数类型重载 operator new/delete,则请采用如下函数声明形式:
// 返回的指针必须能被普通的 ::operator delete(void*) 释放void* operator new(size_t size, const char* file, int line);// 析构函数抛异常时被调用void operator delete(void* p, const char* file, int line);调用时采用以下方式:string* pStr = new (__FILE, __LINE__) string;这样就能跟踪内存分配的具体位置,定位这个动作发生在哪个文件的哪一行代码中了。在“检测内存错误”和“统计内存使用数据”时通常会用这种方式重载。
operator new
(1) 只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则
->如果有new_handler,则调用new_handler,否则
->如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则
->返回0
(2) 可以被重载
(3) 重载时,返回类型必须声明为void*
(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5) 重载时,可以带其它参数
new operator
(1) 调用operator new分配足够的空间,并调用相关对象的构造函数
(2) 不可以被重载
相应地,operator delete与delete operator有相似的特性。
举个例子
class X { public:………… static void* operator new(size_t size){ return ::operator new(size);}static void operator delete(void* pointee){ ::operator delete(pointee);}…………};X* px = new X();
该行代码中的new为new operator,它将调用类X中的operator new,为该类的对象分配空间,然后调用当前实例的构造函数。
delete px;
该行代码中的delete为delete operator,它将调用该实例的析构函数,然后调用类X中的operator delete,以释放该实例占用的空间。
new operator与delete operator的行为是不能够也不应该被改变,这是C++标准作出的承诺。而operator new与operator delete和C语言中的malloc与free对应,只负责分配及释放空间。但使用operator new分配的空间必须使用operator delete来释放,而不能使用free,因为它们对内存使用的登记方式不同。反过来亦是一样。
你可以重载operator new和operator delete以实现对内存管理的不同要求,但你不能重载new operator或delete operator以改变它们的行为。
当重载operator new时,可以提供更多的参数,在new一个对象时,通过在关键字new后的括号传递额外的参数。比如以下的类
class A { public: ………… static void* operator new(size_t size, const string& example){ cout << example << endl; return ::operator new(size);}…………};A* pa = new (“This will be printed out in operator new”) A();
新标准的C++允许以这样的方式传递一个名为nothrow的参数,以表明当为该对象分配空间失败时,不抛出异常,而是返回0,以兼容旧标准new的行为。比如
class B {};B* pb = new (nothrow) B();
当然这只能对那些使用默认operator new操作符的类。对已经重载了operator new的类(比如上面的X和A),如果不声明能接受nothrow参数,自然无法享受C++标准带来的礼物。
operator new的六种重载形式
当写出
p = new P();这样的代码的时候, 实际上有两步操作, 首先分配内存,然后在分配好的内存之上初始化类成员.第二步是有构造函数完成的, 第一步就是new函数的工作.全局的new有六种重载形式, void *operator new(std::size_t count) throw(std::bad_alloc); //一般的版本void *operator new(std::size_t count, //兼容早版本的new const std::nothrow_t&) throw(); //内存分配失败不会抛出异常void *operator new(std::size_t count, void *ptr) throw(); //placement版本void *operator new[](std::size_t count) // throw(std::bad_alloc);void *operator new[](std::size_t count, // const std::nothrow_t&) throw();void *operator new[](std::size_t count, void *ptr) throw();所以, 刚才的用法, 就是使用new函数的一种重载形式.如果A这个对象以同样实行重载了new函数的化, 作为成员函数会被优先调用.C++的各种new简介1.new T第一种new最简单,调用类的(如果重载了的话)或者全局的operator new分配空间,然后用类型后面列的参数来调用构造函数,用法是new TypeName(initial_args_list). 如果没有参数,括号一般可以省略.例如int *p=new int;int *p=new int(10);int *p=new foo("hello");通过调用delete来销毁:delete p;2. new T[]这种new用来创建一个动态的对象数组,他会调用对象的operator new[]来分配内存(如果没有则调用operator new,搜索顺序同上),然后调用对象的默认构造函数初始化每个对象用法:new TypeName[num_of_objects];例如int *p= new int[10];销毁时使用operator delete[]3.new()T 和new() T[]这是个带参数的new,这种形式的new会调用operator new(size_t,OtherType)来分配内存这里的OtherType要和new括号里的参数的类型兼容,这种语法通常用来在某个特定的地址构件对象,称为placement new,前提是operator new(size_t,void*)已经定义,通常编译器已经提供了一个实现,包含<new>头文件即可,这个实现只是简单的把参数的指定的地址返回,因而new()运算符就会在括号里的地址上创建对象需要说明的是,第二个参数不是一定要是void*,可以识别的合法类型,这时候由C++的重载机制来决定调用那个operator new当然,我们可以提供自己的operator new(size_,Type),来决定new的行为,比如char data[1000][sizeof(foo)];inline void* operator new(size_t ,int n){ return data[n];}就可以使用这样有趣的语法来创建对象:foo *p=new(6) foo(); //把对象创建在data的第六个单元上的确很有意思标准库还提供了一个nothrow的实现:void* operator new(std::size_t, const std::nothrow_t&) throw();void* operator new[](std::size_t, const std::nothrow_t&) throw();就可以实现调用new失败时不抛出异常new(nothrow) int(10);// nothrow 是std::nothrow_t的一个实例placement new 创建的对象不能直接delete来销毁,而是要调用对象的析够函数来销毁对象,至于对象所占的内存如何处理,要看这块内存的具体来源4. operator new(size_t)这个的运算符分配参数指定大小的内存并返回首地址,可以为自定义的类重载这个运算符,方法就是在类里面声明加上void *operator new(size_t size){ //在这里分配内存并返回其地址}无论是否声明,类里面重载的各种operator new和operator delete都是具有static属性的一般不需要直接调用operator new,除非直接分配原始内存(这一点类似于C的malloc)在冲突的情况下要调用全局的operator加上::作用域运算符:::operator new(1000); // 分配1000个字节返回的内存需要回收的话,调用对应的operator delete5.operator new[](size_t)这个也是分配内存,,只不过是专门针对数组,也就是new T[]这种形式,当然,需要时可以显式调用6.operator new(size_t size, OtherType other_value)和operator new[](size_t size, OtherType other_value)参见上面的new()需要强调的是,new用来创建对象并分配内存,它的行为是不可改变的,可以改变的是各种operator new,我们就可以通过重载operator new来实现我们的内存分配方案.
--------------------------------------------
1 #include2 #include 3 #include 4 using namespace std; 5 6 class X 7 { 8 public: 9 X(){cout<<"constructor of X"<
在调用前面的两个冒号很关键,是为了避免进行无限递归(没有它函数将一直调用自己下去)。
关于"::"的小插曲:
1 #include2 using namespace std;3 int n = 12;// A global variable4 int main()5 {6 int n = 13;// A local variable7 cout << ::n << endl;// Print the global variable: 128 cout << n << endl;// Print the local variable: 139 }
2019-03-06
是否调用构造函数/析构函数
使用new操作符来分配对象内存时会经历三个步骤:
- 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
- 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
- 第三部:对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
- 第一步:调用对象的析构函数。
- 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。如果你不嫌啰嗦可以看下我的例子:
class A{public: A() :a(1), b(1.11){} private: int a; double b; }; int main() { A * ptr = (A*)malloc(sizeof(A)); return 0; }
在return处设置断点,观看ptr所指内存的内容:
可以看出A的默认构造函数并没有被调用,因为数据成员a,b的值并没有得到初始化,这也是上面我为什么说使用malloc/free来处理C++的自定义类型不合适,其实不止自定义类型,标准库中凡是需要构造/析构的类型通通不合适。
而使用new来分配对象时:
int main() { A * ptr = new A; }
查看程序生成的汇编代码可以发现,A的默认构造函数被调用了:
--------------------------------------------