标题说明了一切。但是,请将string
作为任何课程的占位符。
std::string s1("hello"); // construct from arguments
std::string s2 = "hello"; // ???
std::string s3; // construct with default values
s3 = "hello"; // assign
我想知道s2
的陈述是否与s1
或s3
的陈述相同。
答案 0 :(得分:14)
s2
的情况为copy initialization。这是初始化,而不是s3
的情况。
请注意,对于std::string
,s1
和s2
的效果相同,将调用apporiate构造函数(即std::string::string(const char*)
)来构造对象。但是复制初始化与direct initialization(s1
的情况)之间存在差异;对于复制初始化,不会考虑显式构造函数。假设std::string::string(const char*)
被声明为explicit
,这意味着不允许从const char*
到std::string
的隐式转换;然后第二种情况不会再次编译。
复制初始化比直接初始化更不宽容:显式构造函数不转换构造函数,不考虑复制初始化。
答案 1 :(得分:2)
在这种情况下,s1
和s2
完全 同样的事情:他们都将构造函数调用到const char*
。 (为了清楚起见,有些人更喜欢使用=
。
对于s3
,调用默认构造函数,然后调用operator=
到const char*
。
答案 2 :(得分:2)
虽然所有3种方法的最终结果是相同的(字符串将分配给变量),但是某些基本的差异比语法更深入。我将回顾您的3个字符串所涵盖的所有3种情况:
第一种情况:s1是直接初始化的一个例子。直接初始化涵盖了许多不同的场景,您的定义如下:
使用非空的括号表达式列表进行初始化。
这里,s1没有类数据类型而是std::string
数据类型,因此将进行标准转换以将括号中的数据类型转换为sv的非限定版本s1,即{ {1}}。 Cv-unqualified意味着没有限定符,例如(const)或(volatile)附加到变量。请注意,在直接初始化的情况下,它比复制初始化更宽松,复制初始化是s2的主题。这是因为复制初始化仅引用用户定义的非显式(即隐式)的构造函数和转换函数。另一方面,直接初始化考虑隐式和显式构造函数以及用户定义的转换函数。
继续,第二个字符串s2是复制初始化的一个例子。简单地说,它将值从左侧复制到右侧。这是一个例子:
当声明非引用类型T的命名变量(自动,静态或线程局部)时,初始化程序由等号后跟表达式组成。
此方法涵盖的过程是相同的。由于s2没有类数据类型而是const *char
数据类型,因此它将使用标准转换将右侧字符串的值转换为左侧类型std::string
的值。但是,如果显式声明了该函数,则不能像复制初始化程序那样进行标准转换,并且代码的编译将失败。
查看与2种初始化类型相比较的代码示例。这应该清除上面的任何混淆:
const *char
继续第三种情况,它甚至不是初始化的情况,它是一种分配的情况。就像你在评论中所说的那样,变量s3用默认字符串初始化。该字符串被" Hello"取代当你使用等号时这样做。这里发生的是当在 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时,调用string s3;
的默认构造函数,并设置默认字符串值。当您使用 = 符号时,下一行中的 hello 将替换该默认字符串。
如果我们考虑哪种方式在跑步时的速度更有效,那么差异就是微不足道的。但是,如果我们单独执行此操作,则s1需要最快的运行时间:
std::string
这需要以下时间和内存来编译和运行:
编译时间:0.32秒,绝对运行时间:0.14秒,CPU时间:0秒,内存峰值:3 Mb,绝对服务时间:0,46秒
如果我们查看以下列方式编码的字符串s2:
int main(void)
{
string a("Hello");
}
然后程序运行的总时间是:
编译时间:0.32秒,绝对运行时间:0.14秒,CPU时间:0秒,内存峰值:3 Mb,绝对服务时间:0,47秒
使用复制初始化程序的运行时比直接初始化程序多运行0.01秒。存在差异,但存在差异。
s3的第三种情况,如果按以下方式编码:
int main(void)
{
string a = "Hello";
}
具有完整的运行,编译时间和空间:
编译时间:0.32秒,绝对运行时间:0.14秒,CPU时间:0秒,内存峰值:3 Mb,绝对服务时间:0,47秒
我想在这里指出一些事情:第二种和第三种方法之间的运行时差异很可能是 NOT 零;相反,它是一个小于0.01秒的时间差,第三种方法需要更长的时间(s3)。那是因为它有2行代码可供操作;一个是变量的声明,另一个是字符串赋值给变量。
希望这能回答你的问题。
答案 3 :(得分:1)
如果某个类没有可访问的复制构造函数,则第二种初始化形式无效:
[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
,但仅当副本有效时才会生成。