void * operator new(size_t, void *_Where)同样可以提供自己的版本,这时候第二个参数可以是别的类型,
_Where也不一定是指向一块已分配而未使用的内存,可以是一个指向可以分配内存的函数的指针,然后在
operator new(size_t, void *_Where)内部通过该指针来调用函数从而分配内存。也可以是其他东西,不一定
要是指针。总之可以传递你想传递的东西。下面来看个例子,这个例子来自<Bjarne Stroustrup的FAQ:C++的
风格与技巧>我懒得写了,就用他写的这个.
以下是原文:
有没有“指定位置删除”(placement delete)?
没有,不过如果你需要的话,可以自己写一个。
看看这个指定位置创建(placement new),它将对象放进了一系列Arena中;
class Arena {
public:
void* allocate(size_t);
void deallocate(void*);
// ...
};
void* operator new(size_t sz, Arena& a)
{
return a.allocate(sz); //梁杨注:这里第二个参数传递的是一个引用,
//然后通过其成员函数来分配内存.
}
Arena a1(some arguments);
Arena a2(some arguments);
这样实现了之后,我们就可以这么写:
X* p1 = new(a1) X;
Y* p2 = new(a1) Y;
Z* p3 = new(a2) Z;
// ...
但是,以后怎样正确地销毁这些对象呢?没有对应于这种“placement new”的内建的
“placement delete”,原因是,没有一种通用的方法可以保证它被正确地使用。在C++的
类型系统中,没有什么东西可以让我们确认,p1一定指向一个由Arena类型的a1分派的对象
。p1可能指向任何东西分派的任何一块地方。
然而,有时候程序员是知道的,所以这是一种方法:
template<class T> void destroy(T* p, Arena& a)
{
if (p) {
p->~T(); // explicit destructor call
a.deallocate(p);
}
}
现在我们可以这么写:
destroy(p1,a1);
destroy(p2,a2);
destroy(p3,a3);
如果Arena维护了它保存着的对象的线索,你甚至可以自己写一个析构函数,以避免它发生
错误。
这也是可能的:定义一对相互匹配的操作符new()和delete(),以维护《C++程序设计语
言》15.6中的类继承体系。参见《C++语言的设计和演变》10.4和《C++程序设计语言》
19.4.5。
///
C++中有指定位置创建操作符:new(),但没有指定位置删除操作符:delete()。
我们可以写:
X* p1 = new(a1) X; //指定位置创建
但是不可以写:
delete(a1) p1 //这句不能通过编译。
他上面写的这个程序其实是有点问题的(靠,梁杨你也太牛B了吧,C++之父写的你也敢说有问题?!),用事实说话
,如果只提供 void* operator new(size_t sz, Arena& a)而不提供void operator delete(void* , Arena&)
函数,vs.net的编译器会提出一条警告: “void *operator new(size_t,Arena &)” : 未找到匹配的删除运
算符;如果初始化引发异常,则不会释放内存。
对于new操作符会作三件事,在第一件分配内存顺利完成之后,接着会调用构造函数,如果在调用构造函数中发
生异常,他就会调用operator delete(void*)函数来释放在第一件事中通过operator new(size_t)来分配的内
存,从而保证不会发生内存泄漏。同样定位创建操作符new()也做三件事:第一件调用我们重写的void*
operator new(size_t sz, Arena& a)来分配内存,这步成功之后接着调用构造函数,那么如果在调用构造函数
中发生异常时,怎么办呢?没有相应的释放函数来给编译器调用,所以我们必须自己提供一个void operator
delete(void* , Arena&)来释放内存。这个函数第一个参数的类型必须是void*, 第二个类型必须和operator
new()中的相同,不然当发生异常时,编译器不会调用该函数的。当我们提供了void operator delete(void* ,
Arena&)后还是不能写:delete(a1) pA1这样的语句的。可以写operator delete(pA1, a1),但是最好不要这样
调用,该函数是专为编译器写的,当发生构造异常时调用的。当我们成功的创建一个对象,做完一些事之后,
应该还象他上面写的那样,通过调用destroy(p1,a1);来释放。
注意这里内存分配是在指定创建函数operator new()中调用其他函数来分配的,所以我们需要提供一个相应的
delete( )来预防构造失败时来释放资源。但是在我们前面写的那个共享内存的例子,定位创建函数new()并没
有分配内存,内存分配是在其他地方完成的,这时候我们就不需要提供定位删除函数delete()来释放资源,你
构造函数失败就失败呗,内存又不是你分配的,你无权释放,不可能说你创建一个对象失败了,连内存都释放
了,呆会有权释放内存者(分配者有权释放)再释放一次,那问题就大了。也就是说当我们重写定位创建函数
new()时,如果内存是在其中分配的,那么就要提供相应的delete()函数给编译器专用。反之则不必提供
delete()函数。
四、new(nothrow) MyClass操作符
从字面就可以看出来了,这个版本的new不会抛出异常。他也做三件事,其中第一件调用的就是不抛出异常的
operator new()函数,其原型是:
void *__cdecl operator new(size_t, const std::nothrow_t&)
_THROW0();
看到了,其后面的异常规范是空的,即不抛出任何异常。与常用的那个operator new()函数不同
void *__cdecl operator new(size_t) _THROW1(std::bad_alloc); ,这个可以抛出std::bad_alloc异常。
不允许抛出异常不并代其内部也不会发生异常,当内部内存分配失败时发生异常,因为规范中不允许抛出异常
,这就会抛出一个意外的异常,如果用户不对这个意外异常进行捕捉的话,默认处理是调用abort函数。
五、 new const MyClass
这个版本的new表示在堆内存中创建后个const对象,创建完之后就不能再修改,所以必须在创建时初始化(有无
参构造函数的类除外),并且返回的指针是一个指向const对象的指针,即要写:const int* p = new const
int(1); 而不能写int* p = new const int(1).
好累啊,写了这么多,没精力回头检察了,可能会有一些输入错误,只能请大家见谅了。头好痛。
_Where也不一定是指向一块已分配而未使用的内存,可以是一个指向可以分配内存的函数的指针,然后在
operator new(size_t, void *_Where)内部通过该指针来调用函数从而分配内存。也可以是其他东西,不一定
要是指针。总之可以传递你想传递的东西。下面来看个例子,这个例子来自<Bjarne Stroustrup的FAQ:C++的
风格与技巧>我懒得写了,就用他写的这个.
以下是原文:
有没有“指定位置删除”(placement delete)?
没有,不过如果你需要的话,可以自己写一个。
看看这个指定位置创建(placement new),它将对象放进了一系列Arena中;
class Arena {
public:
void* allocate(size_t);
void deallocate(void*);
// ...
};
void* operator new(size_t sz, Arena& a)
{
return a.allocate(sz); //梁杨注:这里第二个参数传递的是一个引用,
//然后通过其成员函数来分配内存.
}
Arena a1(some arguments);
Arena a2(some arguments);
这样实现了之后,我们就可以这么写:
X* p1 = new(a1) X;
Y* p2 = new(a1) Y;
Z* p3 = new(a2) Z;
// ...
但是,以后怎样正确地销毁这些对象呢?没有对应于这种“placement new”的内建的
“placement delete”,原因是,没有一种通用的方法可以保证它被正确地使用。在C++的
类型系统中,没有什么东西可以让我们确认,p1一定指向一个由Arena类型的a1分派的对象
。p1可能指向任何东西分派的任何一块地方。
然而,有时候程序员是知道的,所以这是一种方法:
template<class T> void destroy(T* p, Arena& a)
{
if (p) {
p->~T(); // explicit destructor call
a.deallocate(p);
}
}
现在我们可以这么写:
destroy(p1,a1);
destroy(p2,a2);
destroy(p3,a3);
如果Arena维护了它保存着的对象的线索,你甚至可以自己写一个析构函数,以避免它发生
错误。
这也是可能的:定义一对相互匹配的操作符new()和delete(),以维护《C++程序设计语
言》15.6中的类继承体系。参见《C++语言的设计和演变》10.4和《C++程序设计语言》
19.4.5。
///
C++中有指定位置创建操作符:new(),但没有指定位置删除操作符:delete()。
我们可以写:
X* p1 = new(a1) X; //指定位置创建
但是不可以写:
delete(a1) p1 //这句不能通过编译。
他上面写的这个程序其实是有点问题的(靠,梁杨你也太牛B了吧,C++之父写的你也敢说有问题?!),用事实说话
,如果只提供 void* operator new(size_t sz, Arena& a)而不提供void operator delete(void* , Arena&)
函数,vs.net的编译器会提出一条警告: “void *operator new(size_t,Arena &)” : 未找到匹配的删除运
算符;如果初始化引发异常,则不会释放内存。
对于new操作符会作三件事,在第一件分配内存顺利完成之后,接着会调用构造函数,如果在调用构造函数中发
生异常,他就会调用operator delete(void*)函数来释放在第一件事中通过operator new(size_t)来分配的内
存,从而保证不会发生内存泄漏。同样定位创建操作符new()也做三件事:第一件调用我们重写的void*
operator new(size_t sz, Arena& a)来分配内存,这步成功之后接着调用构造函数,那么如果在调用构造函数
中发生异常时,怎么办呢?没有相应的释放函数来给编译器调用,所以我们必须自己提供一个void operator
delete(void* , Arena&)来释放内存。这个函数第一个参数的类型必须是void*, 第二个类型必须和operator
new()中的相同,不然当发生异常时,编译器不会调用该函数的。当我们提供了void operator delete(void* ,
Arena&)后还是不能写:delete(a1) pA1这样的语句的。可以写operator delete(pA1, a1),但是最好不要这样
调用,该函数是专为编译器写的,当发生构造异常时调用的。当我们成功的创建一个对象,做完一些事之后,
应该还象他上面写的那样,通过调用destroy(p1,a1);来释放。
注意这里内存分配是在指定创建函数operator new()中调用其他函数来分配的,所以我们需要提供一个相应的
delete( )来预防构造失败时来释放资源。但是在我们前面写的那个共享内存的例子,定位创建函数new()并没
有分配内存,内存分配是在其他地方完成的,这时候我们就不需要提供定位删除函数delete()来释放资源,你
构造函数失败就失败呗,内存又不是你分配的,你无权释放,不可能说你创建一个对象失败了,连内存都释放
了,呆会有权释放内存者(分配者有权释放)再释放一次,那问题就大了。也就是说当我们重写定位创建函数
new()时,如果内存是在其中分配的,那么就要提供相应的delete()函数给编译器专用。反之则不必提供
delete()函数。
四、new(nothrow) MyClass操作符
从字面就可以看出来了,这个版本的new不会抛出异常。他也做三件事,其中第一件调用的就是不抛出异常的
operator new()函数,其原型是:
void *__cdecl operator new(size_t, const std::nothrow_t&)
_THROW0();
看到了,其后面的异常规范是空的,即不抛出任何异常。与常用的那个operator new()函数不同
void *__cdecl operator new(size_t) _THROW1(std::bad_alloc); ,这个可以抛出std::bad_alloc异常。
不允许抛出异常不并代其内部也不会发生异常,当内部内存分配失败时发生异常,因为规范中不允许抛出异常
,这就会抛出一个意外的异常,如果用户不对这个意外异常进行捕捉的话,默认处理是调用abort函数。
五、 new const MyClass
这个版本的new表示在堆内存中创建后个const对象,创建完之后就不能再修改,所以必须在创建时初始化(有无
参构造函数的类除外),并且返回的指针是一个指向const对象的指针,即要写:const int* p = new const
int(1); 而不能写int* p = new const int(1).
好累啊,写了这么多,没精力回头检察了,可能会有一些输入错误,只能请大家见谅了。头好痛。