从 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
当您在 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
时,您实际上是在使用 HugeObject
的 operator=
。这与构造不是一回事。如果 operator=
不是由 class 定义的,那么 C++ 实现会合成它;此默认值通过将基本类型(例如 double
)的右侧对象的每个成员分配给左侧对象上的相应成员来工作。对于存储为 hugeObject,
变量的 class 类型,调用它们对应的 operator=
。
现在直接回答这个问题,如果你想限制你制作的副本,只需将 h 分配给 SetObject
中 hugeObject
的引用,因为两者都已经实例化了:
void SetObject(const HugeObject& hugeObject) {
h = hugeObject;
}
虽然技术上超出范围,const HugeObject&
的值通过 HugeObject
的 operator=
复制到 h
。这不会隐式构造一个新对象,因为两者都已经实例化了;同样,它只是调用 operator=
.
按值:
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
当您在 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
时,您实际上是在使用 HugeObject
的 operator=
。这与构造不是一回事。如果 operator=
不是由 class 定义的,那么 C++ 实现会合成它;此默认值通过将基本类型(例如 double
)的右侧对象的每个成员分配给左侧对象上的相应成员来工作。对于存储为 hugeObject,
变量的 class 类型,调用它们对应的 operator=
。
现在直接回答这个问题,如果你想限制你制作的副本,只需将 h 分配给 SetObject
中 hugeObject
的引用,因为两者都已经实例化了:
void SetObject(const HugeObject& hugeObject) {
h = hugeObject;
}
虽然技术上超出范围,const HugeObject&
的值通过 HugeObject
的 operator=
复制到 h
。这不会隐式构造一个新对象,因为两者都已经实例化了;同样,它只是调用 operator=
.