重载new的分析II

来源:互联网 发布:员工能力矩阵表 编辑:程序博客网 时间:2024/05/21 18:46

作者:baihacker
来源:http://hi.baidu.com/feixue
=============本站原创,转载请注明出处=============
以前写了一篇重载new的分析,很old了,于是有此文.
文档参考:INCITS ISO IEC 14882-2003
编译器:gcc version 4.4.0 (TDM-1 mingw32)
操作系统:windows XP sp3
我人懒,写得少,不懂就问吧
上一篇:http://hi.baidu.com/feixue/blog/item/5c310cd781b4bcdfa144dfc6.html

一.标准解读
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
提纲
3.7.3 Dynamic storage duration(这里主要是介绍一下动态存储)
18.4 Dynamic memory management(这里主要是说明语言支持库中对动态存储管理的支持)
12.5 Free store(对于一个类中定义new和delete的问题)
5.3.4 New(如何使用new)
5.3.5 Delete(如何使用delete)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3.7.3 Dynamic storage duration [basic.stc.dynamic]
1 Objects can be created dynamically during program execution (1.9), using new-expressions (5.3.4), and
destroyed using delete-expressions (5.3.5). A C + + implementation provides access to, and management of,
dynamic storage via the global allocation functions operator new and operator new[] and the
global deallocation functions operator delete and operator delete[].

2 The library provides default definitions for the global allocation and deallocation functions. Some global
allocation and deallocation functions are replaceable (18.4.1). A C + + program shall provide at most one
definition of a replaceable allocation or deallocation function. Any such function definition replaces the
default version provided in the library (17.4.3.4). The following allocation and deallocation functions
(18.4) are implicitly declared in global scope in each translation unit of a program
void* operator new(std::size_t) throw(std::bad_alloc);
void* operator new[](std::size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
void operator delete[](void*) throw();
These implicit declarations introduce only the function names operator new, operator new[],
operator delete, operator delete[]. [Note: the implicit declarations do not introduce the
names std, std::bad_alloc, and std::size_t, or any other names that the library uses to declare
these names. Thus, a new-expression, delete-expression or function call that refers to one of these functions
without including the header <new> is well-formed. However, referring to std, std::bad_alloc, and
std::size_t is ill-formed unless the name has been declared by including the appropriate header. ]
Allocation and/or deallocation functions can also be declared and defined for any class (12.5).
3 Any allocation and/or deallocation functions defined in a C + + program, including the default versions in the
library, shall conform to the semantics specified in 3.7.3.1 and 3.7.3.2.

*****************************************************************************************************************
3.7.3 Dynamic storage duration解读:
概述:说明c++提供了new, delete, new [], delete[]用于在程序执行时动态创建对象,以及new, delete的声明情况
注意点:
1.库中定义了全局的new和delete(在后面提到有12种);
2.库中定义的全局的new和delete是replaceable的,也就是说可以定义有相同声明的函数,来代替这些函数;
3.用于代替这些函数的定义至多一个;
4.这里提到的四种形式的new和delete是默认的,不用#include <new>;
5.如果使用的不是这里的四种形式的new和delete,且没有#include <new>那么这样的程序是ill-formed的.


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18.4 Dynamic memory management [lib.support.dynamic]
1 The header <new> defines several functions that manage the allocation of dynamic storage in a program.
It also defines components for reporting storage management errors.
Header <new> synopsis
namespace std {
class bad_alloc;
struct nothrow_t {};
extern const nothrow_t nothrow;
typedef void (*new_handler)();
new_handler set_new_handler(new_handler new_p) throw();
}
void* operator new(std::size_t size) throw(std::bad_alloc);
void* operator new(std::size_t size, const std::nothrow_t&) throw();
void operator delete(void* ptr) throw();
void operator delete(void* ptr, const std::nothrow_t&) throw();
void* operator new[](std::size_t size) throw(std::bad_alloc);
void* operator new[](std::size_t size, const std::nothrow_t&) throw();
void operator delete[](void* ptr) throw();
void operator delete[](void* ptr, const std::nothrow_t&) throw();
void* operator new (std::size_t size, void* ptr) throw();
void* operator new[](std::size_t size, void* ptr) throw();
void operator delete (void* ptr, void*) throw();
void operator delete[](void* ptr, void*) throw();
SEE ALSO: 1.7, 3.7.3, 5.3.4, 5.3.5, 12.5, 20.4.

*****************************************************************************************************************
18.4 Dynamic memory management解读:
概述:说明了new, delete的12种,以及相应的一些类.
在3.7.3.2中指出对于class也可以用自己的new和delete
注意点:
1.一个常见的误区
int * p = new int; if (p == NULL) {...}
在这里实际调用的是会抛出异常版本的new,也就是说存储分配失败会有bad_alloc的异常,然后再进行是否为
NULL的判断就已经画蛇添足了.
想要通过返回值是不是NULL来判断的话应当使用
int * p = new(nothrow) int; if (p == NULL) {...}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
12.5 Free store [class.free]
1 Any allocation function for a class T is a static member (even if not explicitly declared static).
2 [Example:
class Arena;
struct B {
void* operator new(size_t, Arena*);
};
struct D1 : B {
};
Arena* ap;
void foo(int i)
{
new (ap) D1; // calls B::operator new(size_t, Arena*)
new D1[i]; // calls ::operator new[](size_t)
new D1; // ill-formed: ::operator new(size_t) hidden
}
—end example]
3 When an object is deleted with a delete-expression (5.3.5), a deallocation function
(operator delete() for non-array objects or operator delete[]() for arrays) is (implicitly)
called to reclaim the storage occupied by the object (3.7.3.2).
4 If a delete-expression begins with a unary :: operator, the deallocation function’s name is looked up in
global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a
virtual destructor, the deallocation function is the one found by the lookup in the definition of the dynamic
type’s virtual destructor (12.4).104) Otherwise, if the delete-expression is used to deallocate an object of
class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation
function’s name is looked up in the scope of T. If this lookup fails to find the name, the name is looked up
in the global scope. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement
deallocation function, the program is ill-formed.
5 When a delete-expression is executed, the selected deallocation function shall be called with the address of
the block of storage to be reclaimed as its first argument and (if the two-parameter style is used) the size of
the block as its second argument.105)
6 Any deallocation function for a class X is a static member (even if not explicitly declared static).
[Example:
class X {
// ...
void operator delete(void*);
void operator delete[](void*, size_t);
};
class Y {
// ...
void operator delete(void*, size_t);
void operator delete[](void*);
};
—end example]
7 Since member allocation and deallocation functions are static they cannot be virtual. [Note: however,
when the cast-expression of a delete-expression refers to an object of class type, because the deallocation
function actually called is looked up in the scope of the class that is the dynamic type of the object, if the
destructor is virtual, the effect is the same. For example,
struct B {
virtual ?B();
void operator delete(void*, size_t);
};
struct D : B {
void operator delete(void*);
};
void f()
{
B* bp = new D;
delete bp; //1: uses D::operator delete(void*)
}
Here, storage for the non-array object of class D is deallocated by D::operator delete(), due to the
virtual destructor. ] [Note: virtual destructors have no effect on the deallocation function actually called
when the cast-expression of a delete-expression refers to an array of objects of class type. For example,
struct B {
virtual ?B();
void operator delete[](void*, size_t);
};
struct D : B {
void operator delete[](void*, size_t);
};
void f(int i)
{
D* dp = new D[i];
delete [] dp; // uses D::operator delete[](void*, size_t)
B* bp = new D[i];
delete[] bp; // undefined behavior
}
—end note]
8 Access to the deallocation function is checked statically. Hence, even though a different one might actually
be executed, the statically visible deallocation function is required to be accessible. [Example: for the call
on line //1 above, if B::operator delete() had been private, the delete expression would have
been ill-formed. ]

*****************************************************************************************************************
12.5 Free store解读:
概述:对于class 中定义new, delete 中可能遇到的问题
1.定义的new或者delete是static的,即使不用static说明;
2.查找规则
用限定性名字按限定性规则查找,比如
::new int;
::delete ptr;
XX::operator delete (ptr);
这就指定调用全局或者某个类的相应函数.

非限定性查找
当以delete ptr;或delete[] ptr;的形式调用的时候
首先会在该类里面查找.
对于类中有虚函数,且是单个对象形式的delete 会根据这个对象的实际类型调用其对应的delete.
如果没有虚函数或者是数组形式,那么会根据这个对象的静态类型调用对应的delete.
如第7点所示
B* bp = new D;
bp指向的对象的实际类型是D
在delete的时候也调用D::operator delete
而在第二个例子中,由于是数组形式的,所以有未定义的行为.
当然,如果没有虚函数,就谈不上动态类型了.

总之,先查找类中的,再查找全局的,如果全局的被replace了当然就是replace后的了.
在查找类中的时候,由于多态,可能会根据其对象实际类型调用这个实际类型中的对应函数.

用的是delete ptr;或者delete[] ptr;的形式,最后解析出来的delete是有歧义的,或者是不可访问的,或者是放置的这样的程序是
ill-fromed的.
比如有两个同时满足要求的delete就是有歧义的;
如果对应的delete是protected或者private的那么就不可访问;
如果对应的delete是operator [] (void* ptr, size_t),而用delete ptr;去调用,当然也不行.

二.代码示例

1.replace库中的函数

2.类中的new和delete

对比

在类中,较标准的有
void operator delete(void*);
void operator delete(void*, size_t);
void* operator new (size_t s);
void* operator new (size_t s, void* mem);
可以加入的有
void operator delete (void* ptr, void* mem);
(对于array形式以及其它标准中说明过的形式未提及)

void operator delete(void*);
void operator delete(void*, size_t);
在用delete ptr;形式的时候调用会优先选择第一个.
使用X::operator delete的时候根据参数进行选择.

void* operator new (size_t s);
void* operator new (size_t s, void* mem);
new通过放置语法来进行选择.

三.通过replace实现内存泄漏检测