从 const 引用参数存储成员变量

Storing a member variable from a const reference parameter

按值:

class Test {
private:
    HugeObject h; // Copy 1
public:
    void SetObject(HugeObject hugeObject) { // Copy 2
        h = hugeObject;                     // Copy 3
    }
}

// Somewhere else
Test t();
t.SetObject(HugeObject());

这很糟糕,因为创建了三个隐式副本。如果我将 SetObject() 的参数更改为 const HugeObject& hugeObject,这也很糟糕,因为我将存储一些在该函数范围之外不再存在的东西。

所以,为了防止复制两次和存储无效数据,我可以只复制两次:

void SetObject(const HugeObject& hugeObject) { 
    h = HugeObject(hugeObject); // Copy constructor
}

这是处理这种情况的有效方法还是我误解了什么?愚蠢的优化还是根本不是优化?除了存储指针,还有更好的方法吗?

This is bad because three implicit copies are created.

实际上,在那种情况下只会发生一次复制。 HugeObject() 临时变量作为参数 hugeObject 就地构造,然后从那里复制到 h

If I change the parameter of SetObject() to const HugeObject& hugeObject, it's also bad because I'd be storing something that will no longer exist outside that function's scope.

这也不准确 - 您仍在复制到 h,您没有存储对它的引用。在以下情况下,这将为您节省一份副本:

HugeObject h;
t.SetObject(h); // copy from h into t.h, no copy necessary
                // into the argument of SetObject()

通过const&获取hugeObject是在C++03中做到这一点的最佳方式。如果您可以访问 C++11 编译器,并且您的 HugeObject 可以有效地移动,您应该另外添加一个右值重载:

void setObject(HugeObject&& hugeObject) {
    h = std::move(hugeObject);
}

为什么在构造时获取不到对象?然后你得到原作和副本。

#include <iostream>

using std::cout;

class Huge 
{
public:
    Huge() { cout << "constructor\n"; }
    Huge(const Huge & h) { cout << "copy\n"; }
    Huge & operator = (const Huge & h) { cout << "assignment\n"; return *this; }
};

class Holder
{
    Huge h;
public:
    Holder(const Huge & h_) : h(h_) {};
};

int main()
{
    Huge bar;
    Holder foo(bar);
}

输出:

constructor
copy

Live Example

当您在 class 中首先声明 h 时,您实际做的是调用对象的 默认构造函数 ,这是隐式调用的创建不使用显式语法的对象;也就是说,

HugeObject h; // Calls default constructor implicitly.
HugeObject h2 = HugeObject(); // Still calls default constructor.
HugeObject ougeHobject = HugeObject(h); // Calls copy constructor.
h2 = ougeHobject; // calls HugeObject's operator=

最后一部分是我真正要说的。当您将 h 分配给 SetObject 中的 hugeObject 时,您实际上是在使用 HugeObjectoperator=。这与构造是一回事。如果 operator= 不是由 class 定义的,那么 C++ 实现会合成它;此默认值通过将基本类型(例如 double)的右侧对象的每个成员分配给左侧对象上的相应成员来工作。对于存储为 hugeObject, 变量的 class 类型,调用它们对应的 operator=

现在直接回答这个问题,如果你想限制你制作的副本,只需将 h 分配给 SetObjecthugeObject 的引用,因为两者都已经实例化了:

void SetObject(const HugeObject& hugeObject) {
    h = hugeObject;
}

虽然技术上超出范围,const HugeObject& 的值通过 HugeObjectoperator= 复制到 h。这不会隐式构造一个新对象,因为两者都已经实例化了;同样,它只是调用 operator=.