The easiest way to answer your question is to break down what happens in both cases.
testclass(const string& sref)
testclass t1("constStringRef"); first creates a temporary string object from the const char*
- the constructor is called, the temporary
string object is bound to the constructor's const string& argument
name is uselessly default-constructed since you didn't use the constructor's initializer list (more on that later)
string::operator = is called, making a copy of the const string& argument
Total: 1 copy.
testclass(string str)
testclass t1("constStringRef"); first creates a temporary string object from the const char*
- the constructor is called -- what happens depend on which C++ version you are using:
- C++03: the temporary
string object is copied to the constructor's argument
- C++11: the temporary is moved into the constructor's argument
name is uselessly default-constructed since you didn't use the constructor's initializer list
string::operator = is called, making a copy of the string argument
Total: 2 copies in C++03, 1 copy in C++11.
From this, we could believe that a const string& is better. However this is only true in C++03.
C++11 and move semantics
In C++11, it is better (in this very case) to pass the string by value to the constructor, and then move the argument into your class member:
testclass(string str){
name = std::move(str);
}
Let's see what happens now:
testclass t1("constStringRef"); first creates a temporary string object from the const char*
- the constructor is called, the temporary is moved into the constructor's argument
name is uselessly default-constructed since you didn't use the constructor's initializer list
string::operator = is called, but this time moving the string argument into name
Total: 0 copy!
This is all fine with rvalues, but does this still hold true for lvalues?
string s = "..."; // s has already been constructed some time ago
testclass t1(s); // what happens during this call?
Wrapping it up
In C++03, whether you're passing a lvalue or a rvalue doesn't matter, it is always more efficient to use a const string&. As others have mentioned, you may want to overload your constructor to take a const char* argument and thus avoid a useless copy.
In C++11, provided you move the argument into the member variable, a string argument is the same as a const string& argument for lvalues, but it is more efficient for rvalues (no copy needs to be performed at all). So you should use pass-by-value and then move the argument to the member variable.
Last but not least, you noticed that I insisted on uselessly default-constructing name. To avoid it, use the constructor's initializer list rather than an assignment in the constructor's body:
// 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
nands?!std::moveit in the constructor's initializer list.