std::string, std::function类型是否可以用{0}初始化?
示范代码如下:
struct c_user
{
int id;
char name[64];
};
struct cpp_user
{
int id;
std::string name;
};
int main() {
c_user user1 = {0}; /// c语言常用
cpp_user user2{0}; /// c++11下是否安全?
std::string name{0}; /// gcc没报错,但是否正确?
//std::string name2(0); /// 会崩溃
return 0;
}
最终决定弃用这种用法
之前描述不清楚,补充说明一下:
1 "是否安全",我希望是c++规范明确指出这是一个特性,而不是一个bug。按我的理解, std::string name{0}, std::string name(0)应该是相同的语义,如果会崩溃,两者都应该崩溃。测试其他的string类的实现,如Glib::ustring,Glib::ustring name{0}也是会崩溃的。这说明支持{0}初始化并不是所有string类的通用特性。
2 如果这是一个特性,我也希望个人写的类也有这个特性,如
//期望(0) {0}能调用到不同的构造函数,但不清楚如何实现
class mystring {
public:
//期望{0}能调用此构造函数,但事实上不会
mystring(){
std::cout << "default " << std::endl;
}
// mystring{0} mystring(0)都会调用此构造
mystring(const char *s){
std::cout << "const char " << (void *)s <<std::endl;
}
};
3 如果不能确定,可能用下面的的方式初始化,起码心里有底些
struct cpp_user {
int id{0};
std::string name;
};
如果自己看看是不是安全的,请看汇编,C语言的本质是帮你写汇编的工具,下面我们来解决下面的问题:
cpp_user user2{0}; /// c++11下是否安全?
是安全的,我们来看一下它的反汇编:
001C894A xor eax,eax
001C894C mov dword ptr [user2],eax //ebp-80h
001C894F lea ecx,[ebp-7Ch]
001C8952 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (01C133Eh)
001C8957 mov dword ptr [ebp-4],0
可以看出,你初始化cpp_user
,会主动调用std::string
的构造函数。
std::string name{0}; /// gcc没报错,但是否正确?
没报错的写法都是对的。
std::string name2(0); // 会崩溃
因为它会把0当作地址,调用构造函数进行处理,结果一般的0地址是不可读的。
所有的类型都可以用0初始化,因为在内存都是数据,至于怎么用就是你的问题了。
@岚2022 这么跟你说吧,name(0)
和name{0}
不是一个东西,前者是用0这个地址作为字符串地址为参数调用构造函数,另一个是用0填充为一个字符串,并调用另一个构造函数进行初始化(以字符串作为参数),这个东西完完全全可以通过汇编进行判断。
@寂静的羽夏:"用0填充为一个字符串,并调用另一个构造函数进行初始化"这句话难以理解,可以修改下面代码,使(0) {0}能调用到不同的构造函数吗?
//期望(0) {0}能调用到不同的构造函数,但不清楚如何实现
class mystring {
public:
//期望{0}能调用此构造函数,但事实上不会
mystring(){
std::cout << "default " << std::endl;
}
// mystring{0} mystring(0)都会调用此构造
mystring(const char *s){
std::cout << "const char " << (void *)s <<std::endl;
}
};
@岚2022: 当遇到C语言相关未知的东西的时候,汇编是最好的结论。我上面的所有结论基于汇编。这些事情是编译器为你做的,汇编是直接体现这个东西的。类的{}初始化应该是C++设计编译器的特性,C我记着不能用。汇编是最清楚但最难懂的语言,自己有能力研究研究。我之所以说是不同的构造函数,是因为构造string的函数地址是不一样的。怎么修改实现我不清楚。
@寂静的羽夏: 你这么说也没错,但代价太大,gcc stl库是开源的,也可以直接看stl源码。但这不符合我的需求,我希望代码让人一眼就能看懂,如果大部分人都不明白string{0}的语义,需要翻stl源码去研究,那这个语法就没有使用的必要,换一种大家都能看懂的语法就可以了,比如
struct cpp_user {
int id{0};
std::string name;
};
可以使用cpp_user user2{0};初始化,没有问题的。它会调用这个类的构造函数。
如果尝试下面的代码会输出init。
struct cpp_user
{
int id;
std::string name;
cpp_user(int a){
cout<<"init";
}
};
string同理。
2.
string name2(0);
不可以写,因为string的构造函数的写法是:
string name2("init");
参数可以是一个C风格字符串(也就是char数组 or 指针),也可以是string类。
此时,系统会把0当作地址,也就是NULL地址,当作字符串来读取,NULL是不能读写的,导致运行错误。
std::string 初始化请用 std::string name{}