C++编程规范之45:总是一起提供new和delete

简介:

摘要:

它们是一揽子交易:每个类专门的重载void*operator new(parms)都必须与对应的重载void operator delete(void*, params)相随相伴,其中parms是额外参数类型的一个列表(第一个总是std:size_t)。数组形式的new[]和delete[]也同样如此。

    很少需要提供自定义的new或者delete,但是如果需要其中一个,那么通常两个都需要。如果定义了类专门的T::operator new进行某种特殊的分配操作,很可能还需要定义一个类专门的T::operator delete进行相应的释放操作。

    这些阐述可能有些过于基础了,但是之所以要加入本条款,有一个更加微妙的原因:编译器可能需要T::operator delete的重载,即使实际上从来也不会调用它。这才是为什么要成对提供operator new和operator delete(以及operator new[]和operator delete[])的原因。

    假设定义了一个带有自定义分配操作的类:

class T

{

         //……

         Static void* operator new(std::size_t)

         Static void* operator new(std::size_t,CustomAllocator&)

 

         Static void operator delete(void*,std::size_t)

};

这样就为分配和释放建立了一个简单的协议。

1.调用者能够用默认的分配器(使用new T)或者自定义的分配器,(使用new(alloc)T,其中alloc是一个customAllactor类型的对象)来分配类型T的对象。

2.唯一调用者可能调用的operatordelete是默认的operator delete(size_t),因此当然应该实现,从而能够正确地释放已分配的内存。

到目前为止,一切正常。

但是编译器冉冉需要秘密地调用另一个delete重载,即T::operatordelete(size_t, CustomAllocator&)。这是因为语句

T* p = new(alloc)T;

实际上将扩展为类似下面的代码:

//编译器为T* p =new(alloc)T;生成的代码

//

Void*_compilerTemp = T::operator new (sizeof(T),alloc);

T* p;

Try

{

         P = new(_compilerTemp)T;

}

Catch(…)

{

         T::operator delete(_compilerTemp,sizeof(T), alloc);

         throw

}

    因此,如果分配成功,但是构造函数失败了,那么编译器将顺理成章地自动插入代码,为重载的T::operator new调用对应的T::operator delete。对应的签名是void operator delete(void*,whatever-parameters-new-takes).

    下面是煞风景的部分了。C++ Standard规定,当且仅当operatordelete的重载实际退出时,以上代码才能生成。否则,在构造函数失败的情况下,代码不会调用任何operator delete。也就是说,如果构造函数失败,内存将泄漏。

    正因为如此,重载void*operator new(parms)必须伴有与其对应的重载void operator delete(void* ,params)——因为编译器自己要调用它们。

目录
打赏
0
0
0
0
21
分享
相关文章
|
5月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
123 3
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
140 2
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
126 1
|
6月前
|
C++
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
38 16
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
15天前
|
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
58 6