构造函数参数样式 [英] Constructor parameter style
问题描述
假设我的档案看起来像这样:
#include< iostream>
#include< string>
using namespace std;
class testclass {
public:string name;
// testclass(const string& sref){
// name = sref;
//}
testclass(string str){
name = str;
}
〜testclass(){}
};
int main(){
testclass t1(constStringRef);
cout<< t1.name<< '\\\
';
}
构造函数1和2之间的区别在于以下构造函数:
testclass tobj(tmemberstring);
这是我想到的:
我知道通过引用传递意味着你不传递一个副本,但由于字符串参数,首先有一个字符串初始化(在这两种情况下像一个局部变量,假设),然后是一个对case的引用的初始化1或在case 2的一个新的字符串str的复制。最后,两个构造函数将值复制到成员字符串名称。如果我的想法是正确的,我会跳过一步(复制到字符串str)如果将使用第一个构造函数。
边缘问题:
参数存储在堆栈区?
如果是,这个特定的字符串引用或任何基本数据类型的引用使用多少空间?
希望你的建议,
谢谢
回答你的问题最简单的方法是分解在这两种情况下会发生什么。
testclass(const string& sref)
-
testclass t1(constStringRef);
首先从const中创建一个临时
字符串
char * - 调用构造函数,临时
string
对象绑定到构造函数的const string&
参数 -
名称
调用构造函数的初始化器列表( 更多) -
string :: operator =
const string&
参数的副本
总计: 1个副本。
testclass(string str)
$ b b
-
testclass t1(constStringRef); 首先创建一个临时
字符串
对象从const char *
- 调用构造函数 - 发生什么取决于您使用的C ++版本:
- C ++ 03:将临时
字符串
对象复制到构造函数的参数
- C ++ 03:将临时
-
name
无用的默认构造,因为你没有使用构造函数的初始化列表 -
string :: operator =
总计:在C ++ 03中有2个副本,在C ++ 11中有1个副本。
,我们可以相信一个 const string&
更好。但这只适用于C ++ 03 。
语义
在C ++ 11中,最好(在这种情况下)将字符串的值传递给构造函数,然后将参数移动到类成员:
testclass(string str){
name = std :: move
}
让我们看看现在会发生什么:
-
testclass t1(constStringRef);
首先创建一个临时字符串
对象从const char *
- 调用构造函数,将临时移动到构造函数
-
name
是无用的默认构造,因为您没有使用构造函数的初始化列表 -
string :: operator =
被调用,但这次string
到名称
总计:
这对右值来说是完全正确的,但是对于左值还是如此吗?
string s =... // s已经被构造了一段时间之前
testclass t1(s); //在此通话期间会发生什么?
-
$ c> const string& (在C ++ 03和C ++ 11中):
-
s 绑定到
常数字符串&
参数 - code> name 是无用的默认构造,因为你没有使用构造函数的初始化列表
-
string :: operator = c>参数
- 总计:对于
字符串$ c>的构造函数, >
$>
-
- $ c>然后移动(仅在C ++ 11中):
-
/ code>复制到
字符串
参数 -
是无用的默认构造,因为你没有使用构造函数的初始化列表
-
string :: operator =
将字符串
参数移动到名称
总计: 1份。
-
在C ++ 03 中,无论传递左值还是右值都不重要,使用a const string&
。正如其他人提到的,你可能想重载你的构造函数以获取一个 const char *
参数,从而避免无用的副本。
在C ++ 11 中,如果将参数移动到成员变量中,则 string
参数与< c $ c> const string& lvalues的参数,但是对于右值更有效率(根本不需要执行复制)。因此,您应该使用传递值,然后将参数移动到成员变量。
最后但并非最不重要的,你注意到我坚持无用地默认构造 name
。要避免它,使用构造函数的初始化器列表,而不是在构造函数正文中的赋值:
// C ++ 03
testclass(const char * str):name(str){} //没有复制
testclass(const string& sref):name(sref){} // 1 copy
// C ++ 11
testclass(string str):name(std :: move(str)){} // lvalues的副本,
//没有副本的副本
Lets say my file looks like this:
#include <iostream>
#include <string>
using namespace std;
class testclass{
public: string name;
//testclass(const string& sref){
// name = sref;
//}
testclass(string str){
name = str;
}
~testclass(){}
};
int main(){
testclass t1("constStringRef");
cout << t1.name << '\n';
}
What are the differences between constructor 1 and 2 given the following constructor-call:
testclass tobj("tmemberstring");
Here is what i thought of:
I know that passing by reference means that you don't pass a copy but due to the string-argument there is a string-initialization at first (in both cases treated like a local variable, i assume), which is then followed by a the initialization of a reference to it in case 1 or a copy to a new string str in case 2. In the end both of the constructors copy the values to the member string name. If my thoughts are correct i would skip one step (copying into string str) if would use the first constructor.
Sidequestions: Are arguments stored in the stack area? And if so how much space would this particular string reference or a reference to any basic data types use?
Hoping for your advice, thanks in advance
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 temporarystring
object from theconst char*
- the constructor is called, the temporary
string
object is bound to the constructor'sconst 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 theconst string&
argument
Total: 1 copy.
testclass(string str)
testclass t1("constStringRef");
first creates a temporarystring
object from theconst 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
- C++03: the temporary
name
is uselessly default-constructed since you didn't use the constructor's initializer liststring::operator =
is called, making a copy of thestring
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 temporarystring
object from theconst 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 liststring::operator =
is called, but this time moving thestring
argument intoname
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?
for a constructor that takes
const string&
(both in C++03 and C++11):s
is bound to theconst string&
argumentname
is uselessly default-constructed since you didn't use the constructor's initializer liststring::operator =
is called, making a copy of theconst string&
argument- Total: 1 copy.
for a constructor that takes
string
and then moves it (only in C++11):s
is copied to thestring
argumentname
is uselessly default-constructed since you didn't use the constructor's initializer liststring::operator =
is called, but this time moving thestring
argument intoname
- Total: 1 copy.
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
这篇关于构造函数参数样式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!