I have a Visual Studio 2008 C++ application where a function Foo::CreateBar
uses an internal function to populate a buffer with some data as below. I understand that VS2008 will use return value optimization (RVO) to ensure the Foo::GetData()
call will not incur a copy, but will I incur 开发者_如何学Ccopies for the Bar
constructor? How about for returning the Bar
object from Foo::CreateBar
Is there a more efficient way to do this? Do I need to redefine my Buffer
as boost::shared_ptr< std::vector< BYTE > >
?
typedef std::vector< BYTE > Buffer;
class Bar
{
public:
explicit Bar( Buffer buffer ) : buffer_( buffer ) { };
// ...
private:
Buffer buffer_;
};
class Foo
{
public:
Bar CreateBar() const { return Bar( GetData() ); };
// ...
private:
static Buffer GetData()
{
Buffer buffer;
// populate the buffer...
return buffer;
};
};
Thanks, PaulH
You can answer this question for sure by examining the assembler code that's generated for this source - modify the compiler output options to produce a listing interlaced with the source code.
shared_ptr
is not appropriate, in any case. That's a device for the programmer to manage designs requiring shared objects efficiently, not to fool the compiler into not constructing more objects than it should.
Using a const
reference as parameter of the Bar
constructor
explicit Bar( const Buffer & buffer ) : buffer_( buffer ) { };
will surely avoid you the copy caused by the pass-by-value in the constructor call, but you will have a copy in the construction of the buffer_
field.
shared_ptr
would work. unique_ptr
would work, because ownership is transferred.
You could also use out parameters to avoid the copy. Add a factory function...
class Bar
{
public:
explicit Bar( void (*factory)(Buffer& buffer) ) { factory(buffer_); }
// ...
private:
Buffer buffer_;
};
class Foo
{
public:
Bar CreateBar() const { return Bar( GetData ); }
// ...
private:
static void GetData(Buffer& buffer)
{
// populate the buffer...
}
};
You should make a couple of different tests and see what your compiler does with it. In the code above there are as many as 4 copies of the buffer:
- Inside
GetData
- Return value of
GetData
- Argument of constructor
- Member
The compiler by doing RVO/NRVO can merge 1 and 2 into a single object. Moreover it can merge those two objects with 3 by placing the three objects in the same memory location. What it cannot do is place that object and 4 in the same memory address (in the general case).
On that particular copy, you might elide it by default initializing the internal member vector
(usually no cost) and swapping the contents of the argument with the member:
explicit Bar( Buffer buffer ) // pass by value if your compiler optimizes it
: buffer_() // default construct: no cost
{
buffer_.swap( buffer ); // swap: no cost
};
Also note that this is the whole reason for rvalue references in the upcoming standard, in C++0x (I know that is not available in VS08) the compiler will use move semantics to avoid the copies while maintaining the code readable (readable as in closest to the intentions than to tricks to avoid the cost).
You will definitely incur a copy for the buffer
.
Why do you want to use a shared pointer? Is it a shared data? If so - then go for it. If not - than you have to copy. We don't really know what is your design, do we?
If you're sensitive to copy operations, you may want to force copy operations to be explicit or even prevent them altogether. If you make the copy constructor and assignment operator protected/private (or use boost::noncopyable
) the compiler will produce an error whenever it tries to generate code that copies the object. Of course if you do want to make copies sometimes you'll need to provide a method for explicitly doing so, but that ensures that you never accidentally introduce a copy operation.
精彩评论