3

C++ references are still confusing to me. Suppose I have a function/method which creates an object of type Foo and returns it by reference. (I assume that if I want to return the object, it cannot be a local variable allocated on the stack, so I must allocate it on the heap with new):

Foo& makeFoo() {
    ...
    Foo* f = new Foo;
    ...
    return *f;
}

When I want to store the object created in a local variable of another function, should the type be Foo

void useFoo() {

    Foo f = makeFoo();
    f.doSomething();
}

or Foo&?

void useFoo() {

    Foo& f = makeFoo();
    f.doSomething();
}

Since both is correct syntax: Is there a significant difference between the two variants?

4

3 回答 3

5

是的,第一个将复制返回的引用,而第二个将是对返回的引用makeFoo

请注意,使用第一个版本将导致内存泄漏(很可能),除非您在复制构造函数中使用了一些黑魔法。

好吧,除非你打电话,否则第二个也会导致泄漏delete &f;

底线:不要。随波逐流,按价值回报。或智能指针。

于 2013-01-07T14:57:00.807 回答
3

您的第一个代码做了很多工作:

void useFoo() {
    Foo f = makeFoo();  // line 2
    f.doSomething();
}

想到第 2 行,发生了一些有趣的事情。首先,编译器将发出代码以使用类的默认构造函数构造Foo对象。f然后,它将调用makeFoo(),它还会创建一个新Foo对象并返回对该对象的引用。编译器还必须发出将 的临时返回值复制makeFoo()到对象 at 的代码f,然后它会销毁临时对象。一旦第 2 行完成,f.doSomething()就会调用。但就在useFoo()返回之前,我们也销毁了 处的对象f,因为它超出了范围。

您的第二个代码示例效率更高,但实际上可能是错误的:

void useFoo() {
    Foo& f = makeFoo();   // line 2
    f.doSomething();
}

考虑该示例中的第 2 行,我们意识到我们没有创建对象,f因为它只是一个引用。该makeFoo()函数返回一个新分配的对象,我们保留对它的引用。我们doSomething()通过那个参考来调用。但是当useFoo()函数返回时,我们永远不会破坏makeFoo()为我们创建的新对象并且它会泄漏。

有几种不同的方法可以解决这个问题。如果您不介意额外的构造函数、创建、复制和销毁,您可以只使用第一个代码片段中的引用机制。(如果您有微不足道的构造函数和析构函数,并且没有太多(或没有)要复制的状态,那么这并不重要。)您可以只返回一个指针,这强烈暗示调用者负责管理生命周期被引用对象的循环。

如果您返回一个指针,则暗示调用者必须管理对象的生命周期,但您没有强制执行它。总有一天,某个地方的某个人会出错。因此,您可能会考虑创建一个包装类来管理引用并提供访问器来封装对象的管理。(如果您愿意,您甚至可以将其烘焙到Foo类本身中。)这种类型的包装类在其通用形式中称为“智能指针”。如果您使用的是 STL,您会在模板类中找到一个智能指针 std::unique_ptr实现

于 2013-01-07T15:03:47.623 回答
2

A function should never return a reference to a new object that gets created. When you are making a new value, you should return a value or a pointer. Returning a value is almost always preferred, since almost any compiler will use RVO/NRVO to get rid of the extra copy.

Returning a value:

Foo makeFoo(){
    Foo f;
    // do something
    return f;
}

// Using it
Foo f = makeFoo();

Returning a pointer:

Foo* makeFoo(){
    std::unique_ptr<Foo> p(new Foo());  // use a smart pointer for exception-safety
    // do something
    return p.release();
}

// Using it
Foo* foo1 = makeFoo();                 // Can do this
std::unique_ptr<Foo> foo2(makeFoo());   // This is better
于 2013-01-07T15:27:24.550 回答