我有一个Visual Studio 2008 C ++应用程序,其中函数Foo::CreateBar
使用内部函数用一些数据填充缓冲区,如下所示。我知道VS2008将使用返回值优化(RVO)来确保Foo::GetData()
调用不会产生副本,但是我会为Bar
构造函数生成副本吗?从Bar
返回Foo::CreateBar
对象怎么样?有更有效的方法吗?我是否需要将Buffer
重新定义为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;
};
};
谢谢, PaulH
答案 0 :(得分:3)
您可以通过检查为此源生成的汇编代码来确定回答此问题 - 修改编译器输出选项以生成与源代码交错的列表。
在任何情况下, shared_ptr
都不合适。这是程序员有效管理需要共享对象的设计的一种设备,而不是欺骗编译器不构造更多的对象。
答案 1 :(得分:2)
使用const
引用作为Bar
构造函数的参数
explicit Bar( const Buffer & buffer ) : buffer_( buffer ) { };
肯定会避免由构造函数调用中的pass-by-value引起的副本,但是你将在buffer_
字段的构造中有一个副本。
答案 2 :(得分:1)
shared_ptr
会奏效。 unique_ptr
会起作用,因为所有权已转移。
您还可以使用out参数来避免复制。添加工厂功能...
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...
}
};
答案 3 :(得分:0)
您肯定会为buffer
生成一份副本。
为什么要使用共享指针?它是共享数据吗?如果是这样 - 那就去吧。如果没有 - 比你必须复制。我们真的不知道你的设计是什么,对吗?
答案 4 :(得分:0)
如果您对复制操作很敏感,您可能希望强制复制操作是显式的,甚至完全阻止它们。如果使复制构造函数和赋值运算符受保护/私有(或使用boost::noncopyable
),则编译器在尝试生成复制对象的代码时将产生错误。当然,如果您确实想制作副本,有时您需要提供一种明确的方法,但这样可以确保您不会意外地引入复制操作。
答案 5 :(得分:0)
你应该做几个不同的测试,看看你的编译器用它做什么。在上面的代码中,缓冲区有多达4个副本:
GetData
GetData
通过执行RVO / NRVO的编译器可以将1和2合并为单个对象。此外,它可以通过将三个对象放在同一个内存位置,将这两个对象合并为3。它不能做的是将该对象和4放在相同的内存地址中(在一般情况下)。
在该特定副本上,您可能会默认初始化内部成员vector
(通常没有成本)并使用成员交换参数的内容而忽略它:
explicit Bar( Buffer buffer ) // pass by value if your compiler optimizes it
: buffer_() // default construct: no cost
{
buffer_.swap( buffer ); // swap: no cost
};
另请注意,这是即将推出的标准中 rvalue references 的全部原因,在C ++ 0x中(我知道在VS08中不可用)编译器将使用移动语义以避免副本,同时保持代码可读(可读性最接近意图而非欺骗以避免成本)。