构造函数参数样式 [英] Constructor parameter style

查看:168
本文介绍了构造函数参数样式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我的档案看起来像这样:

  #include< iostream> 
#include< string>

using namespace std;

class testclass {
public:string name;
// testclass(const string& sref){
// name = sref;
//}
testclass(s​​tring 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(s​​tring str)


$ b b

  • testclass t1(constStringRef); 首先创建一个临时字符串对象从 const char *

  • 调用构造函数 - 发生什么取决于您使用的C ++版本:

    • C ++ 03:将临时字符串对象复制到构造函数的参数


    • name 无用的默认构造,因为你没有使用构造函数的初始化列表

    • string :: operator =



总计:在C ++ 03中有2个副本,在C ++ 11中有1个副本。






,我们可以相信一个 const string& 更好。但这只适用于C ++ 03






语义



在C ++ 11中,最好(在这种情况下)将字符串的值传递给构造函数,然后将参数移动到类成员:

  testclass(s​​tring 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 ++ 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(s​​tring 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 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?

  • for a constructor that takes const string& (both in C++03 and C++11):

    • s is bound to the const string& 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 const string& argument
    • Total: 1 copy.
  • for a constructor that takes string and then moves it (only in C++11):

    • s is copied to the string 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: 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆