10

我从主要是 C 和 Java 的背景中学习 C++,我很好奇在 C++ 中返回对象而不必复制对象的最佳方法是什么。据我了解,C++11 引入了右值引用 (&&) 来从临时变量中移动数据(与复制相反)。例子:

std::string getStr(){
   return "Hello";
}

std::string &&hello = getStr();

我能想到的另一种方法是使用共享指针。

std::tr1::shared_ptr<std::string> getStr(){

    std::tr1::shared_ptr<std::string> hello(new std::string("Hello"));
    return hello;

}

auto hello = getStr();

我在想也许右值参考更好,但在我使用它之前我想先征求第二意见。哪个更好?

如果不使用构造函数设置类中的字段,是否也建议它们是右值引用?例子:

class StringHolder{

   public:
     std::string &&str;
};

StringHolder sh;
sh.str = getStr();

非常感谢你们!

4

5 回答 5

11

这个问题可能会作为重复而关闭。这是一个经常被问到的问题。不过我还是想回答一下。

在 C++11 中,当你是一个对象的客户端时std::string,你应该按值传递它,而不用太担心效率:

std::string getStr(){
   return "Hello";
}

std::string hello = getStr();

在这个级别,您不需要关心右值引用。只要知道可以非常有效地std::string从右值(例如从 的返回getStr())中“复制”。此“复制”实际上称为“移动”,并且会自动启用,因为您正在从右值(匿名临时)复制。

不要试图通过恢复引用计数来优化复制。只有在需要共享所有权语义时才使用引用计数。这种说法并不总是正确的。但是对于学习基础知识来说,它已经足够接近了,这是一个很好的经验法则。

当您设计需要按值传递的类时,您需要开始担心右值引用:

class Widget
{
    // pointer to heap data
public:
    // ...
};

对于右值引用和移动语义的简要介绍,N2027是一个不错的教程。N2027 太长,无法在这里粘贴,但足够短,便于阅读。 std::string遵循 N2027 中规定的基础知识,使您能够无罪地传递它。您可以在您的Widget.

于 2012-07-08T00:50:29.023 回答
2

默认情况下,您应该简单地按值传递。如果一个函数不需要修改或复制参数,那么它可以被 接受const&,否则只需按值接受参数。按值返回对象并让它移动语义或 RVO 以避免复制。


C++ 使用并鼓励“按值”传递对象。移动语义是一项功能,它允许代码执行此操作,同时制作的副本比引入它们之前更少。通常代码不需要直接使用右值引用类型来获得移动语义的好处。这是因为临时对象是右值,重载决议将自动选择采用右值引用的方法。

例如:

std::string getStr(){
   return "Hello";
}

std::string hello = getStr();

此代码使用移动语义。return "Hello"临时构造一个字符串,因此用于初始化返回值对象的构造函数将是移动构造函数(尽管实际上返回值优化几乎肯定会避免这种移动)。那么返回的值getStr()是临时的,所以选择的构造函数string hello又是move构造函数。

我认为你也可以这样做std::string &&hello = getStr();,但这可能没有必要,因为hello已经被移动构造了。

我能想到的另一种方法是使用共享指针

我不认为智能指针通常是一个好主意,除非你真的需要它们提供的特定所有权语义。使用简单的“按值”传递是一个很好的默认值,移动语义通常允许您使用它而无需额外复制。

如果不使用构造函数设置类中的字段,是否也建议它们是右值引用?

不,成员引用通常不是一个好主意。它们使事情变得更加复杂,甚至不允许您按照示例所示使用它们。如果您的示例代码被允许,那么sh.str = getStr();将是未定义的行为,因为您试图分配给一个不存在的对象。就像std::string *str; *str = getStr();

于 2012-07-08T02:18:26.637 回答
2

通常,您可以通过返回对成员变量的 const 引用来避免复制。例如:

class Circle {
    Point center;
    float radius;
public:
    const Point & getCenter() const { return center; };
};

这相当于return &center在执行中,实际上很可能会被内联,从而导致调用者直接(只读)访问成员。

在您构造一个临时返回的情况下,编译器可能会省略额外的副本作为优化,您不需要特殊的语法。

于 2012-07-08T00:47:25.900 回答
1

右值引用会快得多,因为它们是一种内在的语言特性,而不是引用计数类。在这里使用智能指针或共享指针不会带来任何好处,并且会大大降低清晰度。但是,右值引用是为此类事物设计的语言的一部分。使用右值引用。

于 2012-07-08T00:42:00.420 回答
0

如果你想在这种情况下使用智能指针,也许 unique_ptr 是一个更好的选择。

于 2012-07-08T00:48:23.340 回答