1

假设我的文件如下所示:

#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)如果会使用第一个构造函数。

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

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

4

4 回答 4

2

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

testclass(const string& sref)

  • testclass t1("constStringRef");首先stringconst char*
  • 构造函数被调用,临时string对象绑定到构造函数的const string&参数
  • name是无用的默认构造,因为您没有使用构造函数的初始化列表(稍后会详细介绍
  • string::operator =被调用,复制const string&参数

总计: 1份。

testclass(string str)

  • testclass t1("constStringRef");首先stringconst char*
  • 构造函数被调用——会发生什么取决于你使用的 C++ 版本:
    • C++03:将临时string对象复制到构造函数的参数
    • C ++ 11:临时被移动到构造函数的参数中
  • name是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
  • string::operator =被调用,复制string参数

总计: C++03 2 份,C++11 1 份。


由此,我们可以相信aconst string&更好。然而,这仅在 C++03 中是正确的


C++11 和移动语义

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

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

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

  • testclass t1("constStringRef");首先stringconst char*
  • 构造函数被调用,临时被移动到构造函数的参数中
  • name是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
  • string::operator =被调用,但这次将参数移动到stringname

总计: 0 份!


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

string s = "..."; // s has already been constructed some time ago
testclass t1(s);  // what happens during this call?
  • 对于采用const string&(在 C++03 和 C++11 中)的构造函数:

    • s绑定到const string&参数
    • name是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
    • string::operator =被调用,复制const string&参数
    • 总计: 1份。
  • 对于获取string然后移动它的构造函数(仅在 C++11 中):

    • s被复制到string参数
    • name是无用的默认构造,因为您没有使用构造函数的初始值设定项列表
    • string::operator =被调用,但这次将参数移动到stringname
    • 总计: 1份。

把它包起来

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

在 C++11中,只要将参数移动到成员变量中,参数与左值的string参数相同const string&,但对右值更有效(根本不需要执行复制)。因此,您应该使用按值传递,然后将参数移动到成员变量。


最后但同样重要的是,您注意到我坚持无用的 default-constructing name。为避免这种情况,请使用构造函数的初始化列表而不是构造函数主体中的赋值:

    // 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
于 2013-06-05T16:54:07.710 回答
1

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

在引用您刚刚创建的临时文件testclass(const string& sref)的情况下,可以使用。在第二种情况下,字符串是按值获取的,因此需要创建第二个临时+。conststring

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

作为一般经验法则,我建议const&尽可能使用。

但是请注意,您可以std::string通过简单地通过模板接受字符串文字来完全避免构建临时文件:

template <size_t N> testclass(const char (&str)[N])
{
  name = str;
}

另请注意,当您的构造函数被调用时,会发生两件事。1)name构件被构造。2)name成员的值发生了变化。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;
}
于 2013-06-05T16:14:58.180 回答
0

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

没错,所以你通常应该喜欢testclass::testclass(const std::string&),因为它避免了那个副本

但由于字符串参数,起初有一个字符串初始化

是的,要么创建一个临时字符串并作为 const ref 传递,要么直接创建参数。

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

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

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


附带问题:参数是否存储在堆栈区域中?

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

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

通常,引用最多可能是指针的大小(并且可能完全省略)。

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

于 2013-06-05T16:23:57.100 回答
0

首先,最好用“std::endl”而不是“'\n'”来完成你的流线。一个构造函数需要一个引用,另一个是一个值,但是您正在传递一个值为“const char *”的 C 字符串。第三:在调用构造函数代码之前初始化你的成员。我会推荐以下内容;

testclass(const char* name) : name(name) {};
于 2013-06-05T16:16:08.813 回答