C++ 学习笔记 (6) 最好不要重载全局 operator new 和 operator delete

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




猜你喜欢

转载自blog.csdn.net/nishisiyuetian/article/details/80244311