回答您的问题的最简单方法是分解两种情况下发生的情况。
testclass(const string& sref)
testclass t1("constStringRef");
首先string
从const char*
- 构造函数被调用,临时
string
对象绑定到构造函数的const string&
参数
name
是无用的默认构造,因为您没有使用构造函数的初始化列表(稍后会详细介绍)
string::operator =
被调用,复制const string&
参数
总计: 1份。
testclass(string str)
testclass t1("constStringRef");
首先string
从const char*
- 构造函数被调用——会发生什么取决于你使用的 C++ 版本:
- C++03:将临时
string
对象复制到构造函数的参数
- C ++ 11:临时被移动到构造函数的参数中
name
是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
string::operator =
被调用,复制string
参数
总计: C++03 2 份,C++11 1 份。
由此,我们可以相信aconst string&
更好。然而,这仅在 C++03 中是正确的。
C++11 和移动语义
在 C++11 中,最好(在这种情况下)将字符串按值传递给构造函数,然后将参数移动到您的类成员中:
testclass(string str){
name = std::move(str);
}
让我们看看现在会发生什么:
testclass t1("constStringRef");
首先string
从const char*
- 构造函数被调用,临时被移动到构造函数的参数中
name
是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
string::operator =
被调用,但这次将参数移动到string
name
总计: 0 份!
对于右值来说这一切都很好,但是对于左值来说这仍然适用吗?
string s = "..."; // s has already been constructed some time ago
testclass t1(s); // what happens during this call?
把它包起来
在 C++03中,无论您传递的是左值还是右值都无关紧要,使用const string&
. 正如其他人所提到的,您可能希望重载构造函数以获取const char*
参数,从而避免无用的副本。
在 C++11中,只要将参数移动到成员变量中,参数与左值的string
参数相同const string&
,但对右值更有效(根本不需要执行复制)。因此,您应该使用按值传递,然后将参数移动到成员变量。
最后但同样重要的是,您注意到我坚持无用的 default-constructing name
。为避免这种情况,请使用构造函数的初始化列表而不是构造函数主体中的赋值:
// C++03
testclass(const char* str) : name(str) {} // no copy
testclass(const string& sref) : name(sref) {} // 1 copy
// C++11
testclass(string str) : name(std::move(str)) {} // 1 copy for lvalues,
// no copy for rvalues