这个问题在这里已有答案:
有a set of good rules to determine whether pass by value or const reference
对于构造函数如下,如何确定它?
class A
{
public:
A(string str) : mStr(str) {} // here which is better,
// pass by value or const reference?
void setString(string str) { mStr = str; } // how about here?
private:
string mStr;
};
您站点的文章不是软件工程的好参考。 (它也可能已过时,因为它涉及移动语义,并且可以追溯到2003年。)
一般规则很简单:通过const引用传递类类型,按值传递其他类型。有明确的例外:为了与标准库的约定保持一致,通常也可以按值传递迭代器和函数对象。
其他任何东西都是优化,在分析器说你必须这样做之前不应该进行。
在这种特殊情况下,假设C ++ 11并移动字符串的构造/赋值,您应该按值获取参数并将其移动到构造函数的成员。
A::A(string str) : mStr(std::move(str)) {}
setter的情况有点棘手,我不确定你是否真的想要/需要优化它的每一点...如果你想要最优化你可以提供两个重载,一个采用右值参考而另一个采取一个恒定的左值参考。无论如何,const lvalue reference可能是一个很好的方法:
void A::setString(string const& str) { mStr = str; }
为什么不同?
对于构造函数,该成员尚未构建,因此需要分配内存。您可以将该内存分配(以及数据的实际复制,但这是leaser cost)移动到接口,这样,如果调用者具有临时性,则可以在没有额外内存分配的情况下转发它。
在分配的情况下,事情有点复杂。如果字符串的当前大小足以容纳新值,则不需要分配,但如果字符串不够大,则需要重新分配。如果将分配移动到接口(按值参数),则即使在不需要时也会一直执行。如果分配是在函数内部(const引用参数)完成的,那么对于一小组情况(那些参数是临时的,大于当前缓冲区的情况),可以完成原本可以避免的分配。
在这种情况下,最好通过const引用传递参数。原因:字符串是类类型,您不需要修改它,它可以是任意大的。
使用成员初始化列表来初始化值总是更好,因为它提供了以下优点:
1)赋值版本,创建一个默认构造函数来初始化mStr,然后在默认构造的值之上分配一个新值。使用MIL避免了这种浪费的构造,因为列表中的参数用作构造函数参数。
2)它是初始化常量变量的唯一地方,除非这些只是可以在类中使用枚举的插入。 enum T {v = 2};
3)这是初始化引用的地方。
这就是我的建议:
#include <iostream>
#include <string>
class A
{
private:
std::string mStr;
public:
A(std::string str):mStr(str){}
//if you are using an older version of C++11, then use
//std::string &str instead
inline const void setString(std::string str)
{
mStr = str;
}
const std::string getString() const
{
return mStr;
}
};
int main()
{
A a("this is a 1st test.");
a.setString("this is a 2nd test.");
std::cout<<a.getString()<<std::endl;
}