与用户定义的new和delete不一致的行为 [英] inconsistent behavior with user-defined new and delete

查看:51
本文介绍了与用户定义的new和delete不一致的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们都知道一个新表达式,


foo * a = new foo();


为单个分配内存然后foo调用foo :: foo()。我们知道




void * p = :: operator new(sizeof(foo));


分配一个sizeof(foo)大小的缓冲区,但不调用foo :: foo()。

所以只有new-expression既可以分配也可以初始化。很公平。


现在假设我们希望使用自定义内存池,


my_pool pool;

foo * a = new(pool)foo();


这个调用


void * operator new(std :: size_t size,my_pool& ;)

throw(std :: bad_alloc);


然后调用foo :: foo()。当然我们也定义了


void operator delete(void * data,my_pool&)throw();


但是我们怎么去打电话给我们的自定义删除?


my_pool pool;

foo * a = new(pool)foo();


//编译错误

删除(池)a;


//调用默认运算符delete(),而不是我们的删除

删除a;


//不调用foo :: ~foo()

:: operator delete(a,pool);


//这是成语吗?

a-> ~foo();

:: operator delete(a ,池);


似乎没有这样的delete-expression来补充

new-expression。为什么不一致?当然这是C ++设计中的疏忽

?特别是我期望删除(池)a。

工作。


回顾一下,我们有一个默认分配然后构造的机制

(无参数) new-expressions)和destruct-then-default-deallocate

(无参数删除表达式)。此外,我们有一个机制

用于自定义分配然后构造(自定义新表达式),但不是

用于destruct-then-custom-deallocate(没有自定义)删除表达式。


一种解决方法是在foo中定义operator new()和operator delete()

,但假设我不能做那。另一种解决方法是使用


模板< typename pool_type,typename T>

void custom_delete(pool_type& pool,T * p)

{

p-> ~T ();

pool.deallocate(p,sizeof(T));

}


但这只是引出了一个问题为什么那应该是必要的,即

为什么没有删除表达式,即删除(池)p。


顺便提一下,这引出了一个更普遍的问题:因为在类之外定义的自定义

运算符new()将无法使用auto_ptr,

人们想知道这样的运算符new()s的重点是什么。当然有人

最终会偶然使用auto_ptr,当从auto_ptr :: ~auto_ptr()调用错误的删除时会造成严重破坏。这似乎是

表明删除p应该自动调用


void operator delete(void * data,my_pool&)throw();


然而这可能无法实现有效(相对于当前的C ++实现)。另一个选择是auto_ptr

构造函数采用可选(de)分配器,但依赖于

程序员记住这样做,但这并没有解决问题但是

只是移动它。


#include< iostream>

#include< ostream>


class my_pool

{

};


void * operator new(std :: size_t size ,my_pool&)

throw(std :: bad_alloc)

{

std :: cerr<< my_pool operator new << std :: endl;

return :: operator new(size);

}


void operator delete(void * data ,my_pool&)throw()

{

std :: cerr<< my_pool operator delete << std :: endl;

return :: operator delete(data);

}


struct foo

{

foo()

{

std :: cerr<< " FOO :: foo的()" << std :: endl;

}


~foo()

{

std :: cerr<< " FOO ::〜FOO()" << std :: endl;

}

};


int main()

{< br $>
my_pool pool;


foo * a = new(pool)foo();


//编译错误

//删除(池)a;


//调用默认运算符delete(),而不是我们的

//删除a;


//不调用foo :: ~foo()

// :: operator delete(a,pool);


//这是成语,那么?

a-> ~foo();

:: operator delete(a,游泳池);


返回0;

}

解决方案

< blockquote> je *************** @ yahoo。 com 写道:


我们都知道一个新表达式,


foo * a = new foo ();


为单个foo分配内存然后调用foo :: foo()。我们知道




void * p = :: operator new(sizeof(foo));


分配一个sizeof(foo)大小的缓冲区,但不调用foo :: foo()。

所以只有new-expression既可以分配也可以初始化。很公平。


现在假设我们希望使用自定义内存池,


my_pool pool;

foo * a = new(pool)foo();


这个调用


void * operator new(std :: size_t size,my_pool& ;)

throw(std :: bad_alloc);


然后调用foo :: foo()。当然我们也定义了


void operator delete(void * data,my_pool&)throw();


但是我们怎么去叫我们的自定义删除?



使用delete运算符不可能。但是,请注意,在对象构造期间抛出异常的情况下,需要为操作员new添加

位置删除副本,在这种情况下

会自动调用它来清理内存。


一种解决方法是定义operator new()和operator delete()

在foo里面,但假设我不能这样做。另一种解决方法是使用


模板< typename pool_type,typename T>

void custom_delete(pool_type& pool,T * p)

{

p-> ~T ();

pool.deallocate(p,sizeof(T));

}



另一个是覆盖全局运算符delete,其中你的

placement new以某种方式将分配器与分配的块相关联,并且删除了相应的分配器并使用它

释放内存。


- 西尔维斯特


5月30日上午10点40分, Sylvester Hesp< s.hes ... @ SPAMoisyn.nlwrote:


>

使用delete运算符是不可能的。但是,请注意,在对象构造期间抛出异常的情况下,需要为操作员new添加

位置删除副本,在这种情况下

会自动调用它来清理内存。


[...]


另一个是覆盖全局运算符删除,你的

放置以某种方式将分配器与分配的块相关联

的内存,删除取出适当的分配器并使用它

释放记忆。



我不知道这是不是一个误会,但我没有使用

贴新品。我正在使用重载运算符new()。我的

new-expression是new(pool)foo(),而placement new是

" new(& pool)foo()" 。


* je *** ************@yahoo.com


5月30日上午10点40分,Sylvester Hesp< s.hes ... @ SPAMoisyn.nlwrote:


>使用删除操作符不可能。但是,请注意,在对象构造期间抛出异常的情况下需要为operator new添加一个
放置删除副本,在这种情况下
会自动调用它来清理内存。



另一个是覆盖全局运算符delete,其中你的
放置以某种方式将分配器与分配的块相关联内存,删除取出适当的分配器并使用它来释放内存。



我不知道这是不是误会,但我没有使用

贴新品。我正在使用重载运算符new()。我的

new-expression是new(pool)foo(),而placement new是

" new(& pool)foo()" 。



误解就在于你:你正在使用新的位置。


-

答:因为它弄乱了人们通常阅读文本的顺序。

问:为什么这么糟糕?

A:热门帖子。

问:usenet和电子邮件中最烦人的是什么?


We all know that a new-expression,

foo* a = new foo() ;

allocates memory for a single foo then calls foo::foo(). And we know
that

void* p = ::operator new(sizeof(foo)) ;

allocates a sizeof(foo)-sized buffer but does NOT call foo::foo().
So only new-expressions both allocate and initialize. Fair enough.

Now suppose we wish to use a custom memory pool,

my_pool pool ;
foo* a = new(pool) foo() ;

This calls

void* operator new( std::size_t size, my_pool & )
throw(std::bad_alloc) ;

and then calls foo::foo(). Naturally we also define

void operator delete( void* data, my_pool & ) throw() ;

But how are we going to call our custom delete?

my_pool pool ;
foo* a = new(pool) foo() ;

// compile error
delete(pool) a ;

// calls the default operator delete(), not our delete
delete a ;

// does not call foo::~foo()
::operator delete(a, pool) ;

// is this the idiom, then?
a->~foo() ;
::operator delete(a, pool) ;

There appears to be no such delete-expression to complement the
new-expression. Why the inconsistency? Surely this is an oversight
in the design of C++? In particular I expected "delete(pool) a" to
work.

To recap, we have a mechanism for default-allocate-then-construct
(no-argument new-expressions) and for destruct-then-default-deallocate
(no-argument delete-expressions). Furthermore, we have a mechanism
for custom-allocate-then-construct (custom new-expressions), but NOT
for destruct-then-custom-deallocate (no custom delete-expressions).

One workaround is to define operator new() and operator delete()
inside foo, but suppose I can''t do that. Another workaround is to use

template< typename pool_type, typename T >
void custom_delete( pool_type & pool, T* p )
{
p->~T() ;
pool.deallocate(p, sizeof(T)) ;
}

but this just begs the question of why that should be necessary, i.e.,
why no delete-expressions, i.e. delete(pool) p.

Incidentally this begs a more general question: since a custom
operator new() defined outside of a class will not work with auto_ptr,
one wonders what is the point of such operator new()s. Surely someone
will eventually use auto_ptr with it by accident, wreaking havoc when
the wrong delete is called from auto_ptr::~auto_ptr(). This seems to
suggest that "delete p" should automagically call

void operator delete( void* data, my_pool & ) throw() ;

however that is probably impossible to implement efficiently (relative
to current C++ implementations). Another option is for the auto_ptr
constructor to take an optional (de)allocator but that relies on
programmers remembering to do so, which doesn''t solve the problem but
just moves it around.

#include <iostream>
#include <ostream>

class my_pool
{
} ;

void* operator new( std::size_t size, my_pool & )
throw(std::bad_alloc)
{
std::cerr << "my_pool operator new" << std::endl ;
return ::operator new(size) ;
}

void operator delete( void* data, my_pool & ) throw()
{
std::cerr << "my_pool operator delete" << std::endl ;
return ::operator delete(data) ;
}

struct foo
{
foo()
{
std::cerr << "foo::foo()" << std::endl ;
}

~foo()
{
std::cerr << "foo::~foo()" << std::endl ;
}
} ;

int main()
{
my_pool pool ;

foo* a = new(pool) foo() ;

// compile error
//delete(pool) a ;

// calls the default operator delete(), not ours
//delete a ;

// does not call foo::~foo()
//::operator delete(a, pool) ;

// is this the idiom, then?
a->~foo() ;
::operator delete(a, pool) ;

return 0 ;
}

解决方案

je***************@yahoo.com wrote :

We all know that a new-expression,

foo* a = new foo() ;

allocates memory for a single foo then calls foo::foo(). And we know
that

void* p = ::operator new(sizeof(foo)) ;

allocates a sizeof(foo)-sized buffer but does NOT call foo::foo().
So only new-expressions both allocate and initialize. Fair enough.

Now suppose we wish to use a custom memory pool,

my_pool pool ;
foo* a = new(pool) foo() ;

This calls

void* operator new( std::size_t size, my_pool & )
throw(std::bad_alloc) ;

and then calls foo::foo(). Naturally we also define

void operator delete( void* data, my_pool & ) throw() ;

But how are we going to call our custom delete?

Not possible using the delete operator. However, do note that a
placement delete counterpart for operator new is needed in the case
that an exception is thrown during object construction, in which case
it is automatically called to clean up the memory.

One workaround is to define operator new() and operator delete()
inside foo, but suppose I can''t do that. Another workaround is to use

template< typename pool_type, typename T >
void custom_delete( pool_type & pool, T* p )
{
p->~T() ;
pool.deallocate(p, sizeof(T)) ;
}

Another one is to override the global operator delete, where your
placement new somehow associates the allocator with the allocated chunk
of memory, and the delete fetches the appropriate allocator and uses it
to free the memory.

- Sylvester


On May 30, 10:40 am, Sylvester Hesp <s.hes...@SPAMoisyn.nlwrote:

>
Not possible using the delete operator. However, do note that a
placement delete counterpart for operator new is needed in the case
that an exception is thrown during object construction, in which case
it is automatically called to clean up the memory.

[...]

Another one is to override the global operator delete, where your
placement new somehow associates the allocator with the allocated chunk
of memory, and the delete fetches the appropriate allocator and uses it
to free the memory.

I don''t know if this was a misunderstanding or not, but I wasn''t using
placement new. I was using an overloaded operator new(). My
new-expression is "new(pool) foo()", whereas placement new is
"new(&pool) foo()".


* je***************@yahoo.com:

On May 30, 10:40 am, Sylvester Hesp <s.hes...@SPAMoisyn.nlwrote:

>Not possible using the delete operator. However, do note that a
placement delete counterpart for operator new is needed in the case
that an exception is thrown during object construction, in which case
it is automatically called to clean up the memory.

[...]

Another one is to override the global operator delete, where your
placement new somehow associates the allocator with the allocated chunk
of memory, and the delete fetches the appropriate allocator and uses it
to free the memory.


I don''t know if this was a misunderstanding or not, but I wasn''t using
placement new. I was using an overloaded operator new(). My
new-expression is "new(pool) foo()", whereas placement new is
"new(&pool) foo()".

The misunderstanding is on your part: you''re using placement new.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?


这篇关于与用户定义的new和delete不一致的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆