重载全局的new和delete

重载全局的new和delete

::operator new ::operator new[] -> 不可以被声明与同一个namespace之内

new会执行三个动作: -> 之前的代码提到:

  • new本身会开辟内存空间.所以声明方法需要一个size_t size的参数

    • inline void* operator new(size_t size) {}

::operator delete ::operator delete[] -> 不可以被声明与同一个namespace之内

  • delete本身是归还内存的动作.所以需要接收一根指针

    • inline void operator delete(void* ptr) {}

上述内容示例代码:

#pragma
#ifndef __NEW_DELETE__
#define __NEW_DELETE__

#include <Windows.h> // 通常来说上面还需要一个宏文件来声明这些不变的系统宏
#include <iostream>

namespace MyDefinition {
void* MyAlloc(size_t size) {
return malloc(size);
}

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

/* 下面两个操作符重载不能声明在一个namespace当中 -> 因为调用的是全局的 */
void* operator new(size_t size) {
std::cout << "MyAlloc global new() \n";
return MyDefinition::MyAlloc(size);
}

//void* operator new[](size_t size) {
//std::cout << "MyAlloc global new() \n";
//return MyDefinition::MyAlloc(size);
//}

void operator delete(void* ptr) {
std::cout << "MyFree global delete() \n";
return MyDefinition::MyFree(ptr);
}

//void operator delete[](void* ptr) {
//std::cout << "MyFree global delete() \n";
//return MyDefinition::MyFree(ptr);
//}
}

#endif // !__NEW_DELETE__

上述是全局的重载

class当中的重载

#pragma
#ifndef __CLASS_NEW_DELETE__
#define __CLASS_NEW_DELETE__

#include <iostream>
#include <Windows.h>

class Foo
{
public:
Foo() {}
// ~Foo() { delete(this); } // 这样声明则会删除指针而不是指针指向的对象
void* operator new(size_t);
void operator delete(void*, size_t);
};

inline void* Foo::operator new(size_t size) {
/* 开辟一块内存空间.返回指针 */
return malloc(size);
}

inline void Foo::operator delete(void* ptr, size_t size) {
free(ptr);
}

#endif // !__CLASS_NEW_DELETE__

调用代码:

#include "new_delete_class.hpp"

int main() {
Foo* p = new Foo();
/*
* new这个动作会做三件事
* 1、声明一根指针.调用自己的new(操作符重载)方法开辟一块内存区域
* 2、将自己的方法指针静态转型为指向Foo的指针
* 3、通过指针调用Foo的构造函数
* try {
* void* mem = operator new(sizeof(Foo));
* p = static_cast<Foo*>(mem);
* p->Foo::Foo(); // 通过指针调用Foo的构造函数
* }
* 可以看到现有内存空间才有的类的构造函数调用
*/
delete p;
/*
* 做两件事情
* 1、通过指针去调用Foo类的析构函数
* 2、通过操作符重载释放掉指针空间
* p->~Foo();
* operator delete(p)
*/
}

另一种操作符重载:

#pragma
#ifndef __CLASS_NEW_DELETE__
#define __CLASS_NEW_DELETE__

#include <Windows.h>

class Foo
{
public:
Foo();
void* operator new[](size_t); // 传入数组
void operator delete[](void*, size_t);
};

inline void* Foo::operator new[](size_t size) {
return malloc(size);
}

inline void Foo::operator delete[](void* ptr, size_t size) {
free(ptr);
}

#endif // !__CLASS_NEW_DELETE__

调用代码:

#include "new_delete_class_more.hpp"

int main() {
Foo* f = new Foo[10]; // 调用10次构造函数 -> 也会开辟10块内存空间

delete[10] f; // 调用10次析构函数
}

上述代码可以看到new这个动作会被分解为三个动作,会回到类当中寻找定义好的代码

强制调用全局的示例代码:

#include "new_delete_class_more.hpp"

int main() {
Foo* fp = ::new Foo;
::delete fp;
/*
* 加了"::"的声明以后会强制调用全局的new和delete
* 如果Foo当中没有定义new和delete也会调用全局的
*/
}

整体类设计示例:

#pragma
#ifndef __CLASS_NEW_DELETE_REAL__
#define __CLASS_NEW_DELETE_REAL__

#include <iostream>
#include <Windows.h>
#include <string>

class Foo
{
public:
Foo(): _id(0) {}
Foo(int i): _id(i) {}

~Foo() {}

/* 新建一个hpp文件创建一块命名空间提供实现 */
static void* operator new(size_t);
static void operator delete(void*, size_t);
static void* operator new[](size_t);
static void operator delete[](void*, size_t);
public:
   /* 下面三个成员变量就是类的大小 */
int _id;
long _data;
std::string _str;
};

#endif // !__CLASS_NEW_DELETE_REAL__

注意:

  • 上述代码当中调用了很多次的构造函数,那么实际开辟内存区域的时候变量size_t的大小会多一个int类型数据大小 (counter计数器)-> 该数据的值是调用的构造函数的次数

  • this指针调用构造函数从counter近的开始.析构函数从举例counter远的开始 -> 这里没有画栈空间图所以口语化表达

可以重载全局、可以重载局部、还可以重载new() -> 注意newnew()的区别 -> 并不是构造方法

重载new()delete() -> 不要和构造函数混淆

特点:

  • 每一个版本的new()的参数列都是独一无二

  • 第一个参数固定是size_t -> 知道类的大小 -> 如果不是编译会报错

  • 其余的参数以new指定的placement arguments为初值

delete():

  • 也可以重载

  • 只有当重载的new()调用构造函数抛出异常的时候(new这个动作被拆解成三步)才会调用这些重载的delete()

  • 主要用来归还未能完全创建成功的object所占用的memory

示例代码:

#pragma
#ifndef __NEW()_DELETE()__
#define __NEW()_DELETE()__

class Foo
{
public:
Foo();
void* operator new(size_t);
void* operator new(size_t, void* star);
void* operator new(size_t, long extra);
void* operator new(size_t, long extra, char init);

/* 重载对于的delete -> 注意这些重载的delete调用的时机 */
void operator delete(void* star); // 一般的delete -> 对应上面第一个new
void operator delete(void*, void*); // 对应上面第二个 -> 第一个void*参数是默认的delete自带的
void operator delete(void*, long); // 对应上面第三个
void operator delete(void*, long, char); // 对应上面第四个
};

#endif // !__NEW()_DELETE()__

调用字符串的new() -> 如Foo * p = new(size) Foo的操作.会返回计数器+size大小的内容 -> 内存模型,拿到的其实是一根指针

  • 这样就实现了悄无声息的多分配一些空间 -> 不仅仅是一根指针大小的空间

  •  

     

posted @ 2024-04-16 22:35  俊king  阅读(75)  评论(0编辑  收藏  举报