[Continued from Learn about new with placement (Part 1)]
Compilers apply the usual rules for argument matching in overload resolution to find an allocation function that will accept this assembled argument list.5 The new-expression won't compile if no operator new is visible that will accept the given arguments.
Placement new
Bjarne Stroustrup, the designer of C++, explained that his primary motivation for extending the syntax of new-expressions was to pass information about where to place the created object.6 Hence, he dubbed the argument list after the keyword new the placement syntax. A new-expression that includes the placement syntax is called new with placement or just placement new.
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 ();
Most C++ implementations define it as an inline function:
inline
void *operator new(std::size_t, void *p) throw ()
{
return p;
}
It does nothing but return the value of its second parameter. It completely ignores its first parameter. The exception-specification throw () indicates that the function isn't allowed to propagate any exceptions.
Programs can use this operator new to construct an object at a particular address. For example, if you declare the timer object as:
extern timer_type the_timer;
you can apply the constructor via the placement new-expression:
new (&the_timer) timer_type;
This placement new-expression compiles into a call to placement operator new, followed immediately by a call to the constructor:
operator new(sizeof(timer_type), &the_timer);
(&the_timer)->timer_type();
Recall that this operator new does nothing but return its second argument, and it's typically defined as an inline function. Most compilers completely eliminate the first statement during optimization.
The second statement:
(&the_timer)->timer_type();
is equivalent to:
the_timer.timer_type();
which applies the timer_type constructor to the_timer. But, again, these notations are just what I'm using to convey the concept of a constructor call. Neither one actually compiles if you try to write it yourself.
If you define the timer as a constant pointer:
timer_type *const the_timer
= reinterpret_cast(0xFFFF6000);
then you can apply the constructor via the placement new-expression:
new (the_timer) timer_type;
This placement new-expression differs from the earlier ones only in that it omits the unary & (address-of) operator from the placement argument: the_timer instead of &the_timer.
The C++ Standard doesn't specify the order of implicit initialization as precisely as some programmers would like. I've heard many embedded developers express a desire for more explicit control over initialization order. Using placement new offers that control.
For example, if you have some master device that must be initialized before some slave device, just write the calls to placement new in that order within some larger initialization function, as in:
void initialize()
{
new (&the_master) master_type;
new (&the_slave) slave_type;
}
Still room for improvement
C++ programmers expect classes to use constructors to perform object initialization. Classes for memory-mapped devices should be no different. However, many common declarations for such objects don't invoke constructors implicitly. Placement new offers a way to invoke constructors explicitly, which might be what you want sometimes, but not necessarily always. At other times, implicit initialization might still be preferable. I'll be looking at other more flexible alternatives in the future.
Endnotes:
1. Saks, Dan, "Memory-mapped devices as C++ classes", Embeddeddesignindia.co.in, March 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7022.HTM.
2. Saks, Dan, "Demystifying constructors," Embeddeddesignindia.co.in, April 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_7113.HTM.
3. . Saks, Dan, "Issues when constructing memory-mapped objects," Embeddeddesignindia.co.in, August 2011. http://forum.embeddeddesignindia.co.in/BLOG_ARTICLE_8898.HTM
4. Saks, Dan. "Throw and catch," ESD, May, 2007, p. 11. www.eetimes.com/4026038.
5. Saks, Dan. "Function Name Overloading," Embedded Systems Programming, May 1999, p. 17. www.eetimes.com/4026899.
6. Bjarne Stroustrup. The Design and Evolution of C++. Addison-Wesley, 1994.
文章评论(0条评论)
登录后参与讨论