tag 标签: member new

相关博文
  • 热度 9
    2011-12-6 10:37
    1602 次阅读|
    0 个评论
      The C++ Standard states that placement allocation functions are reserved, and a C++ program may not define functions that displace the versions in the Standard C++ library. 8 However, a program can overload the placement allocation functions. As diligent reader willc2010 observed in an online comment, "there doesn't seem to be anything to prevent someone from defining this: void * operator new(std::size_t count, timer_type *p) { do_something ; return some_value ; } which can completely, and silently, change the behaviour." Here's why this is a potential problem. You can define a pointer to the timer registers as: timer_type *const the_timer = reinterpret_cast (0xFFFF6000); then initialise the timer using a placement new expression: new (the_timer) timer_type; The compiler selects the placement operator new whose second parameter type most closely matches the type of the placement argument. In this case, compiler selects the overloaded placement new because its second parameter has the same type as the placement argument, the_timer , namely "pointer to timer_type ". Defining operator new as a class member avoids any problems caused by an inappropriate overloading of global placement new . When the compiler sees a new-expression as in: the_timer = new timer_type; it looks first for an operator new that's a member of class timer_type . If the compiler finds any member operator new , it stops looking and never considers any operator new declared globally. In this case, the compiler finds a member declared as: void *operator new(std::size_t); and uses it to compile the new-expression just above. Since timer_type 's member operator new has no placement parameters, a placement new-expression such as: new (the_timer) timer_type; simply won't compile. If you define a usual (non-placement) operator new as a member of class timer_type , and you still want to be able to use placement new-expressions with timer_type , you must define both usual and placement operator new as class members, as in: class timer_type { public: ~~~ void *operator new(std::size_t) { return reinterpret_cast (0xFFFF6000); } void *operator new(std::size_t, void *p) { return p; } ~~~ }; More to come The member operator new defined just above works just fine when you have only one timer. When you have multiple timers at distinct addresses, you need to do something else. Of course, you can always define a placement operator new as a member, but then you don't get guaranteed initialisation. In a future column, I'll consider ways to augment member operator new so that it supports multiple instances of a device. Endnotes: 1. Saks, Dan. "Alternative models for memory-mapped devices," Embedded Design India , March 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_6981.HTM. 2. Saks, Dan. "Memory-mapped devices as C++ classes," Embedded Design India , March 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7022.HTM 3. Saks, Dan. "Compared to what?," Embedded Design India , March 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7029.HTM. 4. Saks, Dan. "Demystifying constructors," Embedded Design India , April 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7113.HTM. 5. Saks, Dan. "Constructors and object definitions," Embedded Design India , April 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7323.HTM. 6. Saks, Dan. "Issues when constructing memory-mapped objects," Embedded Design India , August 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_8898.HTM. 7. Saks, Dan. "Learn about new with placement," Embedded Design India , September 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_9401.HTM. 8. ISO/IEC Standard 14882:2003(E), Programming languages – C++.  
  • 热度 11
    2011-12-6 10:35
    1533 次阅读|
    0 个评论
    I have previously written a number of columns detailing alternative techniques for representing and manipulating memory-mapped devices in C and C++. Due to space limitations, the initial articles left a lot of details unresolved. 1 , 2 , 3 Most of the columns I've written since have focused on filling in the missing details, including a detour into constructors and automatic initialisation. 4 , 5 Classes typically use constructors to perform object initialisation. Classes for memory-mapped devices should be no different. However, as I explained in August, many common declarations for memory-mapped objects don't invoke constructors implicitly. 6 In my most recent column, I explained how you can use new with placement in C++ to invoke constructors explicitly. 7 Explicit constructor calls can be very useful when you need to control initialisation order precisely. However, implicit constructor calls should still be the norm because they provide guaranteed initialisation. This month, I'll show you how to provide guaranteed initialisation for memory-mapped devices by defining operator new as a class member. Where we were For my examples, I've been using a class that represents a programmable timer. The class definition looks like: typedef uint32_t volatile device_register; class timer_type { public: enum { TICKS_PER_SEC = 50000000 }; typedef uint32_t count_type; timer_type() { disable(); } void disable() { TMOD = ~TE; } void enable() { TMOD |= TE; } void set(count_type c) { ... } count_type get() const { ... } private: enum { TE = 0x01 }; device_register TMOD; device_register TDATA; device_register TCNT; }; The class has private data members that represent the timer's device registers, along with public member functions that provide a modest assortment of basic timer operations. One of those operations is a default constructor (highlighted above in red). The Standard C++ Library provides a placement form of operator new declared in the standard header as: void *operator new(std::size_t, void *p) throw (); Calling this function does nothing but return the value of its second argument. Programs can use this placement operator new to construct an object at a particular address. For example, you declare a timer object as: extern timer_type the_timer; and use the linker to place the object in memory. Then you can apply the constructor via the placement new-expression: new (the_timer) timer_type; (I explained the placement new syntax in my previous column. 7 ) If you define the timer using a constant pointer instead: timer_type *const the_timer = reinterpret_cast(0xFFFF6000); then you can apply the constructor via the placement new-expression: new (the_timer) timer_type; Using placement new doesn't provide guaranteed initialisation. By declaring a memory-mapped object and then initializing it using placement new , you can inadvertently do the first step without the second. Fortunately, you can get guaranteed initialisation by using member operator new to combine the steps. Operator new as a class member In applications that use dynamic memory, it often happens that the majority of dynamically-allocated objects are of just a few types. When that is the case, you may be able to achieve significant performance improvements by using special-purpose allocation and deallocation functions for just those few heavily-used types, while still using the library's general-purpose allocation and deallocation functions for all the other (less used) types. C++ lets you implement special-purpose memory managers for objects of a particular class by defining operators new and delete as members of that class, as in: class widget { public: void *operator new(std::size_t n); void operator delete(void *p) throw (); ... }; Thereafter, a new-expression such as pw = new widget; allocates memory using widget::operator new rather than the global operator new . Likewise, delete pw; deallocates memory using widget::operator delete . You can use a member operator new to place and automatically construct a memory-mapped object, as in the timer_type class shown below: class timer_type { public: enum { TICKS_PER_SEC = 50000000 }; typedef uint32_t count_type; void *operator new(std::size_t) { return reinterpret_cast (0xFFFF6000); } timer_type() { disable(); } ~~~ }; This operator new behaves like (global) placement new in that it places the object at a specified address. However, a new-expression that calls this member new doesn't use the placement syntax. You can invoke this member operator new by using an ordinary-looking new-expression, such as: timer_type *const the_timer = new timer_type; This new-expression uses the timer_type 's operator new to place the timer_type object in its memory-mapped location, and uses the timer_type 's default constructor to initialise the object automatically. Pretty neat, huh? Name lookup In addition to providing guaranteed initialisation, using a member operator new instead of placement new avoids a potential mishap. Once again, the Standard C++ Library provides a placement form of operator new declared as: void *operator new(std::size_t, void *p) throw (); Calling this function does nothing but return the value of its second parameter. As I explained in my previous column, most C++ implementations define it as an inline function: inline void *operator new(std::size_t, void *p) throw () { return p; }