自从尝试重载 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 ;
}
} ;