C++ Placement语法解析[译]

来源:互联网 发布:微信收费数据共享 编辑:程序博客网 时间:2024/05/16 18:25

Placement syntax

From Wikipedia, the free encyclopedia

  (Redirected from Placement new)

In the C++ programming languageplacement syntax allows programmers to explicitly specify the memory management of individual objects — i.e. their "placement" in memory. The "placement" versions of the new and delete operators and functions are known as placement newand placement delete.[1]

C++编程语言中,placement语法使得程序员能够明确地指定单个对象的内存管理,比如,他们在内存中的“位置”。new, delete操作符以及函数的”placement”版本被称为placement newplacement delete.

A new expression, placement or otherwise, calls a new function, also known as an allocator function, whose name is operator new. Similarly, a delete expression calls a delete function, also known as a deallocator function, whose name is operator delete.[2][3]

一个new表达式,placement或者其他叫做new 函数,也被称为内存分配函数,或者new操作符。同样的,一个delete表达式叫做delete函数,也被称为内存释放函数,或者delete操作符。

Any new expression that uses the placement syntax is a placement newexpression, and any operator new or operator delete function that takes more than the mandatory first parameter ( size_t and void *, respectively) is a placement new or placement delete function.[4]

一个使用了placement语法的new表达式叫做一个placement new 表达式,使用多个参数的new 操作符或者delete操作符是一个placement new或者placement delete 函数。

Contents

[hide]

·             1 Expressions

o                1.1 Compiler quirks

·             2 Functions

·             3 Use

o                3.1 Default placement

o                3.2 Preventing exceptions

o                3.3 Custom allocators

o                3.4 Debugging

·             4 Placement delete

·             5 References

·             6 Further reading

 [edit] Expressions表达式

The Standard C++ syntax for a non-placement new expression is[2]

用于非placement new 表达式的标准C++语法是:

new new-type-id ( optional-initializer-expression-list )

The placement syntax adds an expression list immediately after the newkeyword. This expression list is the placement. It can contain any number of expressions.[2][3][5]

placement 语法在new关键字添加表达式列表,这个表达式列表就是placement.它可以包含任何数目的表达式。

new ( expression-list ) new-type-id ( optional-initializer-expression-list )

There is no placement delete expression.[6]

没有placement delete 表达式。

[edit] Compiler quirks 编译器相关

Versions of the g++ compiler prior to version 2.3.1 accept a different syntax for placement new expressions:[7]

g++ 2.3.1版本以前的编译器可以使用一种不同的placement new 表达式语法:

new { expression-list } new-type-id ( optional-initializer-expression-list )

Up until version 2.2.2, this was the only placement syntax that that compiler would accept.[7]

直到2.2.2版本,

new ( expression-list ) new-type-id ( optional-initializer-expression-list )

才是编译器唯一可以接受的Placement new 表达式语法。

[edit] Functions 函数

The placement new functions are overloads of the non-placement new functions. The declaration of the non-placement new functions, for non-array and array new expressions respectively, are:[8][9]

Placement new 函数是非placement new函数的重载函数。Non-placement针对非数组和数组的new表达式分别是:

void * operator new (size_t)throw(std::bad_alloc);
void * operator new[] (size_t)throw(std::bad_alloc);

The Standard C++ library provides two placement overloads each for these functions. Their declarations are:[8][9]

标准的C++库对这些上述函数分别提供两个对应的placement重载函数:

void * operator new (size_tconststd::nothrow_t &) throw();
void * operator new (size_tvoid *) throw();
void * operator new[] (size_tconststd::nothrow_t &) throw();
void * operator new[] (size_tvoid *)throw();

In all of the overloads, the first parameter to the operator new function is of type size_t, which when the function is called will be passed as an argument the amount of memory, in bytes, to allocate. All of the functions must return type void *, which is a pointer to the storage that the function allocates.[2]

在所有的重载函数中,new操作符函数中的第一个参数的类型是size_t, 在函数调用的时候,将作为申请内存数目的参数进行传递,申请内存大小以byte为单位进行分配。所有的函数必须返回一个void*类型的指针,用于指向分配获得的内存空间。

 

There are also placement delete functions. They are overloaded versions of the non-placement delete functions. The non-placement delete functions are declared as:[8][9]

同样也有placement delete 函数, 他们是non-placement delete函数的重载版本,非placement delete 函数的delete函数申请为:

void operator delete (void *) throw();
void operator delete[] (void *) throw();

 

The Standard C++ library provides two placement overloads each for these functions. Their declarations are:[8][9]

标准C++库同样也提供了针对上述两个函数的重载版本,它们被声明如下形式:

 

void operator delete (void *, conststd::nothrow_t &) throw();
void operator delete (void *, void *) throw();
void operator delete[] (void *, conststd::nothrow_t &) throw();
void operator delete[] (void *, void *)throw();

In all of the overloads, the first parameter to the operator delete function is of type void *, which is the address of the storage to deallocate.[2]

在所有的重载函数中,delete操作符函数第一个参数的类型是void*,用于指定释放内存的地址。

 

For both the new and the delete functions, the functions are global, are not in any namespace, and do not have static linkage.[2]

对于newdelete函数,它们是全局的,不在任意一个命名空间,也没有静态链接。

[edit] Use 使用

Placement syntax has four main uses: default placement, preventing exceptions, custom allocators, and debugging.

Placement语法有四个主要的用法:默认placement, 防止异常,自定义分配子,以及调试。

[edit] Default placement 默认placement

The placement overloads of operator new and operator delete that employ an additional void * parameter are used for default placement, also known as pointer placement. Their definitions by the Standard C++ library, which it is not permitted for a C++ program to replace or override, are:[8][9][10]

应用一个额外的void*参数的new操作符和delete操作符的placement 重载参数用于默认placement,也叫做指针placement. 它们是根据标准C++库进行的定义,不允许C++程序替换或者覆盖:

void * operator new (size_tvoid * p) throw(){ return p ; }
void * operator new[] (size_tvoid * p)throw() { return p ; }
void operator delete (void *, void *) throw(){ }
void operator delete[] (void *, void *)throw() { }

Default placement does not require the inclusion of the Standard C++ library header <new> in the source code of a C++ program.[2]

默认的placement不要求在C++程序中包含标准C++库头文件<new>

There are various uses for default placement.

默认placement有有多的应用。

Bjarne Stroustrup originally observed, in his book The Design and Evolution of C++, that pointer placement new is necessary for hardware that expects a certain object at a specific hardware address. It is also required for the construction of objects that need to reside in a certain memory area, such as an area that is shared between several processors of a multiprocessor computer.[11]

Bjarne Stroustrup 在他的“C++的设计及演化”一书中提到,指针placement new操作符对于那些在特定硬件地址上期许某个对象的硬件是必须的。它同时也要求创建需要存在在某个内存区域中的对象,如多处理器计算机中几个处理器共享的一个区域。

Other uses, however, include calling a constructor directly, something which the C++ language does not otherwise permit.[3]

其他的使用包含直接调用构造函数,和其他一些C++语言不支持的不同方式。

The C++ language does allow a program to call a destructor directly, and, since it is not possible to destroy the object using a delete expression, that is how one destroys an object that was constructed via a pointer placement new expression. For example:[12][6]

C++语言确实允许程序直接调用析构函数,同时由于不可能使用delete表达式销毁对象(这是销毁通过指针placement new表达式建立起来的对象的一种方式)如:

p->~T() ;

[edit] Preventing exceptions 防止异常

Normally, the (non-placement) new functions throw an exception, of typestd::bad_alloc, if they encounter an error, such as exhaustion of all available memory. This was not how the functions were defined by Stroustrup's Annotated C++ Reference Manual, but was a change made by the standardization committee when the C++ language was standardized. The original behaviour of the functions, which was to return aNULL pointer when an error occurred, is accessible via placement syntax.[3][5][4]

正常情况下,非placement new 函数在碰到一个错误(如可用内存的耗尽)扔出一个类型为std::bad_alloc异常。这个函数的定义和Stroustrup的“C++注解”一书中所做的定义是不同的,而是C++语言标准化组织所做的更改。原来函数碰到错误的时候,就会返回一个NULL指针。

 

Programmers that wish to do this in their programs must include the Standard C++ library header <new> in the source code. This header declares the global std::nothrow object, which is of type std::nothrow_t(also declared in the header), which is used to call the overloaded new functions that are declared as taking const nothrow_t & as their second parameter. For example:[10]

程序员要想在自己的程序中使用这种功能的话,就必须在程序的源文件中包含<new>头文件。这个头文件声明了全局std::nothrow对象,其类型为std::nothrow_t(同样也在头文件中被声明)。这个对象用于调用使用const notrhow_t& 作为其第二个参数的new重载函数,如:

#include <new>

struct T {} ;

int main ()
{
    
// Call the function operator new(size_t, const nothrow_t &) and (if successful) construct the object.
    T * p = new (std::nothrow) T ;
    
if (p) {
        
// The storage has been allocated and the constructor called.
        delete p ;
    
} else
        ; // An error has occurred.  No storage has been allocated and no object constructed.
    return 0 ;
}

[edit] Custom allocators  定制分配子

Placement syntax is also employed for custom allocators. This does not use any of the allocator and deallocator functions from the Standard C++ library header <new>, but requires that programmers write their own allocation and deallocation functions, overloaded for user-defined types. For example, one could define a memory management class as follows:[9][8]

placement 语法也能用于定制分配子。它并不使用标准C++库头文件<new>中的任何分配子和解析子,而是需要程序员编写自己的分配和解析函数,重载用户自定义的类型,如可以定义如下的内存管理类:

#include <cstdlib>
class A {
public:
    
void * allocate ( size_t ) ;
    
void deallocate ( void * ) ;
} ;

And define custom placement allocation and deallocation functions as follows:[9][8]

自定义placement 分配和析构函数如下:

void *
operator 
new (size_t size, A & arena)
{
    
return arena.allocate(size) ;
}
void
operator delete (void * p, A & arena)
{
    arena.
deallocate(p) ;
}

The program would employ the placement syntax to allocate objects using different instances of the A class as follows:[9][8]

程序会使用如下类型A的不同实例通过placement语法来分配对象:

A first_arena, second_arena ;
T * p1 = 
new (first_arena) T ;
T * p2 = 
new (second_arena) T ;

Destroying an object whose storage is allocated in such a fashion requires some care. Because there is no placement delete expression, one cannot use it to invoke the custom deallocator. One must either write a destruction function that invokes the custom deallocator, or call the placement delete function directly, as a function call.[6][9][8]

销毁一个通过如上方式分配内存的对象需要特别注意。由于不存在用户自定义的delete表达式,所以就无法调用用户自定义的析构子。解决方案就是要么在析构函数中调用自定义的析构子,要么就直接调用placement delete函数。

The former would resemble:[9]

和上面的代码相似

void
destroy (T * p, A & arena)
{
    p->~T
() ;   // First invoke the destructor explicitly.
    arena.deallocate(p) ;  // Then call the deallocator function directly.
}

which would be invoked from a program as:

它可以在函数中通过如下方式被调用:

A arena ;
T * p = 
new (arena) T ;
/* ... */
destroy(p, arena) ;

The latter would involve simply writing the destructor invocation and delete function call into the program:[8][13]

前者(原文是后者,笔者认为这里应该是前者)简化了编写析构函数调用代码和delete函数。

A arena ;
T * p = 
new (arena) T ;
/* ... */
p->~T() ;    // First invoke the destructor explicitly.
operator delete(p, arena) ;   // Then call the deallocator function indirectly via operator delete(void *, A &) .

A common error is to attempt to use a delete expression to delete the object. This results in the wrong operator delete function being called. Dewhurst recommends two strategies for avoiding this error. The first is to ensure that any custom allocators rely upon the Standard C++ library's global, non-placement, operator new, and are thus nothing more than simple wrappers around the C++ library's memory management. The second is to create new and delete functions for individual classes, and customize memory management via class function members rather than by using the placement syntax.[13]

通常出现的错误是试着使用delete表达式来删除对象。它会导致调用了错误的delete 操作符函数。Dewhurst推荐了两种规避这种错误的策略,第一个是,保证任何自定义分配子依赖于标准C++库的全局,非placementnew操作符,这样的话,就能保证使用C++库内存管理的简单的容器。第二种方法是为每个独立的类创建和newdelete函数,并且通过类中的成员函数而非使用placement语法来自定义内存管理。

[edit] Debugging 调试

Placement new can also be used as a simple debugging tool, to enable programs to print the filename and line number of the source code where a memory allocation has failed. This does not require the inclusion of the Standard C++ library header <new>, but does require the inclusion of a header that declares four placement functions and a macro replacement for the new keyword that is used in new expressions. For example, such a header would contain:[10][14]

placement new 能用于简单的调试工具,以便输出应用程序内存分配出错的源文件的文件名以及源代码所在的行数。这无需包含标准C++库头<new>, 但是却必须包含一个声明了四个placement 函数以及用于替换new的一个宏的头文件,如:

#if defined(DEBUG_NEW)
void * operator new (size_t size, const char*fileint line);
void * operator new[] (size_t size, constcharfileint line);
void operator delete (void * p, const char*fileint line);
void operator delete[] (void * p, const char*fileint line);
#define New new(__FILE__, __LINE__)
#else
#define New new
#endif

This would be employed in a program as follows:[10][14]

在程序中,可以这样用:

T * p = New T ;

The custom-written placement new functions would then handle using the supplied file and line number information in the event of an exception. For example:[10][14]

用户自定义的placement new函数在发生异常的时候,就会使用提供的文件名和行数来处理异常事件。如:

#include <new>
#include <cstdlib>

class NewError {
public:
    NewError
(const char * fileint line) { /* ... */ }
    
/* ... */
} ;

void *
operator 
new (size_t size, const charfile,int line)
{
    
if (void * p = ::operator new (size, std::nothrow))
        
return p ;
    throw NewError
(file, line) ;
}

[edit] Placement delete

As noted above, there is no placement delete expression. It is not possible to call any placement operator delete function using a deleteexpression.[6][15]

如上所述,不存在placement delete expression. 无法通过使用delete表达式来调用任何placement delete 操作符函数。

The placement delete functions are called from placement newexpressions. In particular, they are called if the constructor of the object throws an exception. In such a circumstance, in order to ensure that the program does not incur a memory leak, the placement delete functions are called. A placement new expression first calls the placement operator newfunction, then calls the constructor of the object upon the raw storage returned from the allocator function. If the constructor throws an exception, it is necessary to deallocate that storage before propagating the exception back to the code that executed the placement new expression, and that is the purpose of the placement delete functions.[6][2][4][15]

placement delete函数由placement new 表达式进行调用。尤其是,如果对象的创建扔出了一个异常的话,它们就会被调用以释放分配的内存。在这种情况下,为了使得应用程序不至于引起一个内存泄漏,就会调用placement delete函数。Placement new 表达式首先调用placement new操作符函数,然后在分配函数中返回的初始内存上调用对象的构造函数。如果对象的构造函数抛出了一个异常,在异常蔓延到执行placement new表达式的代码时,构造函数有必要抛出一个异常,这也就是placement delete函数的意义所在。

The placement delete function that is called matches the placement new function that was invoked by the placement new expression. So, for example, if the following code is executed, the placement delete function that is called will be operator delete(void *, const A &):[6][2][15]

调用的placement delete函数和由placement new expression调用的placement new 函数必须匹配。所以,如下的代码,调用的placement delete函数将是operator delete(void* , const A&)

#include <cstdlib>

struct A {} ;
struct E {} ;

class T {
public:
    T
() { throw E() ; }
} ;

void * operator new ( size_tconst A & ) ;
void operator delete ( void *, const A & ) ;

int main ()
{
    A a ;
    T * p = 
new (a) T ;
    
return 0 ;
}

This is why the pointer placement delete functions are defined as no-operations by the Standard C++ library. Since the pointer placement new functions do not allocate any storage, there is no storage to be deallocated in the event of the object's constructor throwing an exception.[6]

这也是为什么指针placement delete函数被标准C++库定义为无操作,因为指针placement new函数无需分配任何空间,在对象构造函数扔出异常的时候也就没有内存可以释放了。

If no matching placement delete function exists, no deallocation function is called in the event of an exception being thrown by a constructor within a placement new expression. There are also some (older) C++ implementations that do not support placement delete (which, like the exception-throwing allocator functions, were an addition made to C++ when it was standardized) at all. In both such situations, an exception being thrown by a constructor when allocating using a custom allocator will result in a memory leak. (In the case of the older C++ implementations, a memory leak will also occur with non-placement new expressions.)[4][15]

如果不存在匹配的placement delete函数,在placement new表达式中调用的构造函数引发异常时将不会调用析构函数。还有一些(老的)C++ 实现,他们根本不支持placement delete(就像一场处理分配函数函数一样,是在C++标准化以后才添加上去的)。在以上两种情况下,在通过使用自定义分配子函数分配内存空间,调用构造函数时发生的异常将会导致内存泄漏。(在老式C++实现中,内存泄漏在非-placement new 表达式中也会发生。)

[edit] References

  1. ^ "Placement New/Delete". C++ Language and Library. Glen McCluskey & Associates LLC (2000-06-26). Retrieved on 2008-11-26.
  2. a b c d e f g h i Ray Lischner (2003). C++ in a Nutshell. O'Reilly. pp. 72–73,128–129,310,623–625. ISBN 059600298X.
  3. a b c d Stanley B. Lippman (1997). C++ Gems. Cambridge University Press. pp. 386–389. ISBN 0135705819.
  4. a b c d Scott Meyers (1998-04-01). "Placement new and placement delete", Dr. Dobb's Journal, United Business Media LLC.
  5. a b Kyle Loudon (2003). C++ Pocket Reference. O'Reilly. pp. 109–110. ISBN 0596004966.
  6. a b c d e f g Nicholas Solter and Scott Kleper (2005). Professional C++. Wiley. pp. 458–461. ISBN 0764574841.
  7. a b Joe Buck (1997-05-12). "3.4. g++ won't accept the placement new syntax.". Frequently asked questions about the GNU C++ compiler. Retrieved on 2008-11-26.
  8. a b c d e f g h i j Dirk Vermeir (2001). Multi-paradigm Programming Using C++. Springer. pp. 113–115. ISBN 1852334835.
  9. a b c d e f g h i j Stroustrup, Bjarne (1997). The C++ Programming Language (Third Edition ed.). Addison-Wesley. pp. 255–256,576.ISBN 9780201889543.
  10. a b c d e Gail Anderson (1998). "Object Storage Management".Navigating C++ and Object-oriented Design. Prentice Hall. pp. 345–356. ISBN 0135327482.
  11. ^ Stroustrup, Bjarne (1994). "Memory Management". Design and Evolution of C++Addison-WesleyISBN 9780201543308.
  12. ^ Graham M. Seed and Barry J. Cooper (2001). An Introduction to Object-oriented Programming in C++ (Second Edition ed.). Springer. pp. 435–436. ISBN 1852334509.
  13. a b Stephen C. Dewhurst (2003). "Gotcha #62: Replacing Global New and Delete"C++ Gotchas. Addison-Wesley. pp. 173–176.ISBN 9780321125187.
  14. a b c Wu Yongwei (2007-12-31). "A Cross-Platform Memory Leak Detector". Wu Yongwei's Programming Page. Retrieved on 2008-11-26.
  15. a b c d Gail Anderson (1998). "Exception Handling". Navigating C++ and Object-oriented Design. Prentice Hall. pp. 631–632. ISBN 0135327482.

[edit] Further reading

·             Franek, Frantisek (2004). Memory as a Programming Concept in C and C++Cambridge University PressISBN 9780521520430.

·             Marshall Cline (2006-09-25). "11.10: What is "placement new" and why would I use it?". C++ FAQ Lite. Retrieved on 2008-11-26.

·             "C++ new Operator". IBM's Mac OS X compilersIBM (2003). Retrieved on 2008-11-27.

·             "The operator new Function". MSDNMicrosoft. Retrieved on2008-11-27.

·             "new Operator (C++)". MSDNMicrosoft. Retrieved on 2008-11-27.

·             Danny Kalev (1998-07-18). "Placement new". Tip of the day. Jupitermedia Corporation. Retrieved on 2008-11-27.

Retrieved from "http://en.wikipedia.org/wiki/Placement_syntax"

 

0 0
原创粉丝点击