void MyClass::MyClass(const char *cstr) : mStdString( cstr ? cstr : "") {} void MyClass::MyClass(const char *cstr) : mStdString(cstr ? std::string(cstr) : std::string()) {} void MyClass::MyClass(const char *cstr) { if (cstr) mStdString = cstr; // else keep default-constructed mStdString }
编辑,类MyClass中的构造函数声明:
MyClass(const char *cstr = NULL);
这些或其他可能的其中一个是从可能的NULL指针初始化std :: string的最佳或最适当的方式,为什么?不同的C标准是不同的?假设正常的版本构建优化标志.
我正在寻找一个答案,解释为什么一种方式是正确的方式,或一个答案与参考链接(这也适用,如果答案是“无关紧要”),而不仅仅是个人意见(但如果你必须,至少让它只是一个评论).
解决方法
前两个在语义上完全相同(想到c_str()成员函数),所以更喜欢第一个版本,因为它是最直接和惯用的,最容易阅读.
(如果std :: string有一个constexpr的默认构造函数,会有一个语义上的差异,但是std :: string()与std :: string(“”)不同,但是我可以不知道这样做的任何实现,因为它似乎没有什么意义,另一方面,流行的小字符串优化现在意味着这两个版本可能不会执行任何动态分配.)
更新:正如@Jonathan指出的那样,两个字符串构造函数可能会执行不同的代码,如果这对你很重要(尽管不是这样),你可能会考虑第四个版本:
: cstr ? cstr : std::string()
可读和默认构造.
第二次更新:但更喜欢cstr? cstr:“”.如下所示,当两个分支都调用相同的构造函数时,可以非常有效地使用条件移动和无分支来实现. (所以这两个版本确实产生不同的代码,但是第一个更好)
对于giggles,我已经运行两个版本通过Clang 3.3,与-O3,在x86_64,一个结构foo;像你的和一个函数foo bar(char const * p){return p; }:
默认构造函数(std :: string()):
.cfi_offset r14,-16 mov R14,RSI mov RBX,RDI test R14,R14 je .LBB0_2 mov RDI,R14 call strlen mov RDI,RBX mov RSI,R14 mov RDX,RAX call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm jmp .LBB0_3 .LBB0_2: xorps XMM0,XMM0 movups XMMWORD PTR [RBX],XMM0 mov QWORD PTR [RBX + 16],0 .LBB0_3: mov RAX,RBX add RSP,8 pop RBX pop R14 ret
空字符串构造函数(“”):
.cfi_offset r14,RDI mov EBX,.L.str test RSI,RSI cmovne RBX,RSI mov RDI,RBX call strlen mov RDI,R14 mov RSI,RBX mov RDX,RAX call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm mov RAX,R14 add RSP,8 pop RBX pop R14 ret .L.str: .zero 1 .size .L.str,1
在我的情况下,甚至会出现“”生成更好的代码:两个版本调用strlen,但空字符串版本不使用任何跳转,只有条件移动(因为调用相同的构造函数,只有两个不同的参数) .当然,这是一个完全无意义的,不可移植的和不可转移的观察,但它只是表明编译器并不总是需要像你想象的那样多的帮助.只需编写看起来最好的代码.