自从尝试重载 new 操作符,也算是见了眼界——原来 new 是不可以重载的。
一般使用的分配内存的 new 就是一个操作符,不可改变,不可重载
可以重载的是 operator new 和 placement new ( placement new 重载了 operator new )
std::string *One = new std::string ( "YHL" ) ;new 分三步
1. 调用 operator new 申请一片内存,类似于 malloc
2. placement new 在这块内存上建立一个对象
3. placement new 返回对象的指针
对应 delete 就有 operator delete , 不过没有 placement delete
1. 调用对象的析构函数
2. operator delete 释放对象的空间
利用 operator new 和 operator delete 两个函数,可以有助于调试和内存管理,例如,利用 C++ 编译器内置的宏 __FILE__ , __LINE__ , __FUNCTION__ 可以跟踪内存,打印文件,所在行,所在函数等。
一般重载 operator new 和 operator delete 最好在类内部,最好不要重载全局的 operator new 和 operator delete,虽然看起来没错,但是会和 STL 内部冲突,出现出乎意料的情况
std::string *One = new std::string ( "YHL" ) ;上面这句话,在重载了全局的 operator new 和 operator delete 会出现奇怪的现象,特此记下:
程序结果,对于标准库内置的 string(以 string * 的形式 new 分配) 或者是智能指针 unique_ptr 或者是 shared_ptr , 会出现 new 一次,析构两次的现象,如下:
而内置的 Int 数据类型,自定义的数据类型则正常。
虽然没有报错,但是可以发现,string 内部的构造函数有新的 new......
最好不要在全局重载 operator new 和 operator delete , 因为会和 STL 库的 alloctor 有冲突(string 的构造函数内部用了 alloctor , 有点复杂)
如果要重载,应该在类内部重载,不过得调用全局的 ::operator new 和 ::operator delete ,这样不仅正确,而且不会因重复定义而死循环,在重载函数里跟踪。再一来,不会因全局的 new 重载污染了全局的 operator new
代码如下,以此警戒!
#include <bits/stdc++.h> #define rep( i , j , n ) for ( int i = int(j) ; i < int(n) ; ++i ) #define dew( i , j , n ) for ( int i = int(n-1) ; i > int(j) ; --i ) #define _PATH __FILE__ , __LINE__ typedef std::pair < int , int > P ; using std::cin ; using std::cout ; using std::endl ; using std::string ; void* operator new ( size_t size , const char *file , unsigned int line ) { if ( void *ptr = malloc ( size ) ) { // cout << "文件 : " << file << endl ; // cout << "第 " << line << " 行" << endl ; cout << endl << "new 一次" << endl ; return ptr ; } throw std::bad_alloc () ; } void operator delete ( void *ptr ) { if ( ptr == nullptr ) return ; cout << ptr << "\tsource has been released !" << endl ; free ( ptr ) ; // ptr = nullptr ; } #define new new ( __FILE__ , __LINE__ ) class Gragh { public: int data ; explicit Gragh () : data ( 0 ) { // cout << "默认构造函数" << endl ; } explicit Gragh ( int _data ) : data ( _data ) { // cout << "构造函数" << endl ; } ~Gragh () noexcept { // cout << "析构函数" << endl ; } } ; int main () { // string 只是一个 char * 指针, 所以一直是 8 字节 cout << endl << "第一个 string" ; string *One = new string ( "YHL" ) ; // cout << *One << endl ; // cout << "one = " << One << endl ; delete One ; One = nullptr ; cout << endl << "第二个 string" ; string *Four = std::move ( new string ( "YHL" ) ) ; // cout << *Four << endl ; delete Four ; Four = nullptr ; cout << endl << "int" << endl ; int *Five = new int ( 6 ) ; // cout << "Five = " << *Five << endl ; delete Five ; cout << endl << "我自定义的数据结构" ; Gragh *Two = new Gragh ( 100 ) ; // cout << "Two = " << Two->data << endl ; delete Two ; cout << endl << "STL 库的智能指针" ; std::shared_ptr<int> Three ( new int ( 100 ) ) ; // cout << *Three << endl ; // 栈内存, 不会 delete // Gragh Three ( 200 ) ; // cout << "Three = " << Three.data << endl ; return 0 ; }
从上面也可以猜测, C++ 的智能指针可能是用模板类封装了指针, new 之后自动调用 delete, 而且会调用全局重载的 operator delete
正确的重载 opertor new 和 operator delete 应该是在自定义类的内部,而且调用全局的 operator new 和 operator delete ( 用 malloc 和 free 也可以),以避免和 STL 许多和 alloctor 有关的而引起奇怪现象
class Gragh { public: int data ; explicit Gragh () : data ( 0 ) { cout << "默认构造函数" << endl ; } explicit Gragh ( int _data ) : data ( _data ) { cout << "构造函数" << endl ; } ~Gragh () noexcept { cout << "析构函数" << endl ; } void* operator new ( size_t size , const char *file , unsigned int line ) { if ( void* ptr = ::operator new ( size ) ) { cout << "文件 : " << file << endl ; cout << "第 " << line << " 行" << endl ; return ptr ; } throw std::bad_alloc () ; } void operator delete ( void *ptr ) { if ( ptr == nullptr ) return ; cout << ptr << "\tsource has been released !" << endl ; ::operator delete ( ptr ) ; ptr = nullptr ; } } ;