在c ++中有效地传递和返回对象

时间:2011-04-25 19:22:12

标签: c++ optimization return-value

我有一个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

6 个答案:

答案 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个副本:

  1. 内部GetData
  2. GetData
  3. 的返回值
  4. 构造函数的参数
  5. 会员
  6. 通过执行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中不可用)编译器将使用移动语义以避免副本,同时保持代码可读(可读性最接近意图而非欺骗以避免成本)。