构造函数参数样式

时间:2013-06-05 16:05:34

标签: c++ methods parameters reference constructor

让我们说我的文件是这样的:

#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';
}

给定以下构造函数调用,构造函数1和2之间有什么区别:

testclass tobj("tmemberstring");

以下是我的想法:

我知道通过引用传递意味着你没有传递副本但是由于字符串参数最初有一个字符串初始化(在两种情况下都被视为局部变量,我假设),然后在情况1中初始化对它的引用或在情况2中复制到新的字符串str。最后,两个构造函数都将值复制到成员字符串名称。如果我的想法是正确的,我会跳过一步(复制到字符串str)如果使用第一个构造函数。

Sidequestions: 参数是否存储在堆栈区域中? 如果是这样,这个特定的字符串引用或对任何基本数据类型的引用会占用多少空间?

希望得到你的建议, 提前谢谢

4 个答案:

答案 0 :(得分:2)

回答问题的最简单方法是分解两种情况下发生的情况。

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个副本。


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


C ++ 11并移动语义

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

    testclass(string str){
        name = std::move(str);
    }

让我们看看现在发生了什么:

  • testclass t1("constStringRef");首先从string
  • 创建一个临时const char*对象
  • 调用构造函数,临时将移动到构造函数的参数中
  • 由于你没有使用构造函数的初始化列表,
  • name是无用的默认构造
  • string::operator =被调用,但这次将<{1}}参数移动到string

总计: 0副本!


对于左值,这一切都很好,但对于左值这仍然适用吗?

name
    对于采用string s = "..."; // s has already been constructed some time ago testclass t1(s); // what happens during this call? (在C ++ 03和C ++ 11中)的构造函数,
    • const string&绑定到s参数
    • 由于你没有使用构造函数的初始化列表,
    • const string&是无用的默认构造
    • 调用
    • name,制作string::operator =参数
    • 的副本
    • 总计: 1份。
  • 获取const string&然后移动的构造函数(仅限C ++ 11):

    • string被复制到s参数
    • 由于你没有使用构造函数的初始化列表,
    • string是无用的默认构造
    • name被调用,但这次将<{1}}参数移动到string::operator =
    • 总计: 1份。

将其包装

在C ++ 03 中,无论是传递左值还是右值都无关紧要,使用string总是更有效率。正如其他人所提到的,您可能希望重载构造函数以获取name参数,从而避免使用无用的副本。

在C ++ 11 中,如果将参数移动到成员变量中,const string&参数与lvalues的const char*参数相同,但它是对于右值更有效(根本不需要复制)。所以你应该使用pass-by-value,然后将参数移动到成员变量。


最后但并非最不重要的是,您注意到我坚持使用无用的默认构建string。要避免它,请使用构造函数的初始化列表而不是构造函数体中的赋值:

const string&

答案 1 :(得分:1)

在这两种情况下,构造函数都接受std::string。由于您使用字符串文字(const char*)调用构造函数,因此将构造临时std::string以调用构造函数。这两种方法的区别在于接下来会发生什么:

如果testclass(const string& sref) const对您刚刚创建的临时string进行了const&引用。在第二种情况下,字符串是按值获取的,因此 second 临时需要+才能创建。

  • 注意:编译器有时可以优化第二个临时值。

作为一般经验法则,我建议尽可能使用std::string

但请注意,只需通过模板接受字符串文字,即可完全避免构造临时template <size_t N> testclass(const char (&str)[N]) { name = str; }

name

另请注意,当您的构造函数被调用时,会发生两件事。 1)构造name成员。 2)name成员的值被更改。您可以使用初始化列表在一个步骤中初始化和构造template <size_t N> testclass(const char (&str)[N]) : name (str, N-1) // minus one to not copy the trailing `\0`. Optional, depending { name = str; } 成员:

{{1}}

答案 2 :(得分:0)

首先,使用“std :: endl”而非“'\ n'”更好地完成流线。 一个构造函数需要一个引用,另一个需要一个值,但是您传递的是一个值为“const char *”的C-String。 第三,在调用Constructor-Code之前初始化你的成员。 我会推荐以下内容;

testclass(const char* name) : name(name) {};

答案 3 :(得分:0)

  

我知道通过引用传递意味着您没有传递副本

这是对的,所以你通常应该更喜欢testclass::testclass(const std::string&),因为它避免了这个副本

  

但是由于字符串参数,首先是字符串初始化

是的,有一个临时字符串被创建并作为const引用传递,或者参数是直接创建的。

但是还有另一个字符串:您的name成员默认初始化,只是因为您没有使用初始化列表。这样:

testclass::testclass(const std::string &s) : name(s) {}

直接初始化name ,而不先默认初始化它,然后在构造函数体中更改它。


  

Sidequestions:参数是否存储在堆栈区域?

参数可以保存在堆栈中,也可以保存在寄存器中,或者以编译器发现符合标准的任何其他方式保存。这是一个实现细节。

  

...如果是这样,这个特定的字符串引用或对任何基本数据类型的引用会占用多少空间?

一般来说,引用可能最多指针的大小(并且可能完全被省略)。

你没有考虑过的事情是std::string拥有动态分配的字符数组副本,所以你传递的每个std::string都可以执行动态分配,然后是超出范围时的分配。 (有些编译器可能会通过引用计数来避免这种情况,但我们会重新讨论实现细节。)