`string s("hello");` 和 `string s = "hello";` 有区别吗

Is there a difference between `string s("hello");` and `string s = "hello";`

标题说明了一切。但是,请将 string 作为任何 class.

的占位符
std::string s1("hello");  // construct from arguments
std::string s2 = "hello"; // ???
std::string s3;           // construct with default values
s3 = "hello";             // assign

我想知道 s2 的语句是否与 s1s3 的语句相同。

在这种情况下,s1s2 完全相同的事情:它们都调用 const char* 的构造函数。 (为了清楚起见,有些人更喜欢使用 =)。

对于s3,调用默认的构造函数,后面是operator=const char*.

s2的情况是copy initialization。它是初始化,而不是 s3.

的赋值

注意std::strings1s2效果相同,会调用相应的构造函数(即std::string::string(const char*))构造对象.但是copy initialization和direct initialization是有区别的(s1的情况);对于复制初始化,将不考虑显式构造函数。假设 std::string::string(const char*) 声明为 explicit,这意味着不允许从 const char*std::string 的隐式转换;那么第二种情况将不会再次编译。

Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.

如果class没有可访问的复制构造函数,则第二种初始化形式无效:

[temp]$ cat test.cpp
struct S {
    S(int);
private:
    S(const S&);
};

S s1(3);  // okay
S s2 = 3; // invalid
[temp]$ clang++ -std=gnu++1z -c test.cpp
test.cpp:8:3: error: calling a private constructor of class 'S'
S s2 = 3; // invalid
  ^
test.cpp:4:5: note: declared private here
    S(const S&);
    ^
1 error generated.
[temp]$ 

区别在于,第二个在形式上创建了一个 S 类型的临时对象,用值 3 初始化,然后将该临时对象复制到 s2 中。允许编译器跳过复制并直接构造 s2,但 仅当 复制有效时。

虽然所有 3 种方法的最终结果都相同(字符串将分配给变量),但存在某些比语法更深层次的基本差异。我将介绍您的 3 个字符串涵盖的所有 3 个场景:

第一种情况:s1是直接初始化的例子。直接初始化涵盖了许多不同的场景,您的定义如下:

initialization with a nonempty parenthesized list of expressions.

这里,s1没有class数据类型,而是std::string数据类型,所以会进行标准转换,将括号中的数据类型转换为cv-unqualified ] s1 的版本,即 const *char。 Cv-unqualified 表示变量没有附加限定符,例如 (const)(volatile)。请注意,在直接初始化的情况下,它比 copy-initialization 更宽松,这是 s2 的主题。这是因为 copy-initialization 将仅引用用户定义的 non_explicit(即隐式)的构造函数和转换函数。另一方面,direct-initialization 考虑隐式和显式构造函数以及 user-defined 转换函数。

继续,第二个字符串 s2 是复制初始化的示例。简单地说,它将值从左侧复制到右侧。这是一个例子:

when a named variable (automatic, static, or thread-local) of a non-reference type T is declared with the initializer consisting of an equals sign followed by an expression.

这个方法涵盖的过程是一样的。由于 s2 没有 class 数据类型而是 std::string 数据类型,因此它会使用标准转换将右侧字符串的值转换为 const *char 类型的值在左侧。但是,如果显式声明该函数,则无法像复制初始值设定项那样进行标准转换,代码编译将失败。

查看一些与 2 种初始化类型进行比较的代码示例。这应该清除上面的任何混淆:

    struct Exp { explicit Exp(const char*) {} }; // This function has an explicit constructor; therefore, we cannot use a copy initialization here
    Exp e1("abc");  // Direct initialization is valid here
    Exp e2 = "abc"; // Error, copy-initialization does not consider explicit constructor
     
    struct Imp { Imp(const char*) {} }; // Here we have an implicit constructor; therefore, a copy initializer can be used
    Imp i1("abc");  // Direct initialization always works
    Imp i2 = "abc"; // Copy initialization works here due to implicit copy constructor 

接着第三种情况,它甚至不是初始化的情况,而是赋值的情况。正如您在评论中所说,变量 s3 是使用默认字符串初始化的。当您使用等号这样做时,该字符串将替换为 "Hello"。这里发生的是,当在 string s3; 中声明 s3 时,调用 std::string 的默认构造函数,并设置默认字符串值。当您使用 = 符号时,该默认字符串在下一行中被替换为 hello

如果我们在 运行ning 时查看哪个在速度方面更有效,差异很小。然而,如果我们只这样做,s1 花费最快的时间 运行:

    int main(void)
    {
        string a("Hello");
    }

这需要以下时间和内存来编译和 运行:

Compilation time: 0.32 sec, absolute running time: 0.14 sec, cpu time: 0 sec, memory peak: 3 Mb, absolute service time: 0,46 sec

如果我们查看按以下方式编码的字符串 s2:

    int main(void)
    {
        string a = "Hello";
    }

那么程序到运行的总耗时是:

Compilation time: 0.32 sec, absolute running time: 0.14 sec, cpu time: 0 sec, memory peak: 3 Mb, absolute service time: 0,47 sec

使用复制初始化器的 运行 时间比直接初始化器多 运行 0.01 秒。存在差异,但微不足道。

第三种情况,s3,如果编码如下:

    int main(void)
    {
        string a;
        a = "Hello";
    }

总共 运行ning,编译时间和 space 占用:

Compilation time: 0.32 sec, absolute running time: 0.14 sec, cpu time: 0 sec, memory peak: 3 Mb, absolute service time: 0,47 sec

我想在这里指出一点:第二种和第三种方法之间的运行时间差很可能不是零;相反,它是一个小于 0.01 秒的时间差,第三种方法需要更长的时间(s3)。那是因为它有两行代码可以操作;一个是变量的声明,另一个是将字符串赋值给变量。

希望这能回答您的问题。