C++构造函数和隐式字符串转换

6
在c++中,我可以编写一个带有构造函数的类,该构造函数接受std::string参数。这将允许我从std::stringchar *构建此类的实例,由于隐式转换。
是否有必要同时拥有std::string构造函数和char *构造函数呢?
class MyStringClass {
 public:
    MyStringClass( const std::string &str ); // char *'s could implicitly use this constructor
    MyStringClass( const char * str );       // would this ever be necessary?
};

这个问题同样适用于函数参数。
void do_stuff_with_string( const std::string &str );
void do_stuff_with_string( const char * str );

编辑:

为了澄清,我更关注性能。假设这些构造函数/函数调用到只接受char *的API。如果我不需要构造std::string,是否值得拥有这两个单独的函数?

void do_stuff_with_string( const std::string &str )
{
    do_stuff_with_string( str.c_str() );
}

void do_stuff_with_string( const char * str )
{
    // call some api that only accepts char *
}

1
显而易见的原因是如果你想要以不同的方式处理它们。当然,还有其他原因,这使得回答这个问题变得困难,而不能简单地提供一个列表。你能否给我们更多关于你正在尝试做什么的信息? - Cody Gray
@CodyGray 我想更多地询问性能方面的问题。如果在函数内部仅使用 .c_str() 方法(例如),是否值得同时拥有两者以避免构造字符串,还是开销如此微小,我不必担心它? - Brian Schlenker
正如@CodyGray所指出的,您所节省的是字符串的构建。这种开销是否“微不足道”取决于类在程序中的使用方式,因此很难在这里一般性地回答。 - Xavier Leclercq
如果你有一个_const char*_字符串,然后通过隐式转换创建一个_const std::string&_,最终通过_std::string::c_str()_将其传递给一些具有_const char*_接口的C-API,那么这绝对是一个值得质疑的设计。请参见下面的答案以获取详细信息。 - plexando
3个回答

3

如果您希望处理C字符串和std::string的方式不同,那么您需要重载构造函数。

MyStringClass::MyStringClass( const std::string &str )
{
    // Do std::string-specific stuff here.
}

MyStringClass::MyStringClass(const char * str )
{
    // Do char* specific stuff here.
}

还有一种可能性是,const char *类型的参数不是以null结尾的C字符串,而是指向单个字符或非以null结尾的字符数组。在这种情况下,隐式转换可能会失败。

示例:

#include <iostream>

int DoStuff(const std::string &myString)
{
    std::cout << myString << std::endl;
}

int main()
{
    DoStuff("This is a null terminated c-string");  // Fine!

    char charArray[] = { 'A', 'B', 'C' };           // Not null terminated!
    DoStuff(charArray);                             // Uh oh!
}

上面的示例是针对一个函数的,但同样适用于构造函数。上述示例编译时不会出现警告!
就性能而言,由于std::string(const char * const)构造函数将复制c字符串到其自己的内部缓冲区中,因此肯定会有一些影响。然而,在大多数情况下,影响将是可以忽略不计的,因为复制非常高效。但对于非常大的字符串来说,这可能是个问题。
作为一般规则,请尽可能使用C++字符串,并在需要C风格字符串时使用std::string::c_str()成员。从char*到std::string的偶尔字符串拷贝在大多数情况下都只是微小的优化。只有在非常关键的性能代码中,这才会成为一个潜在的问题。

最后一个例子无论如何都是一个“// 哎呀!”。因为您没有将数组的长度作为参数传递,所以您将直接读取到末尾。函数知道这一点的唯一方法是定位一个哨兵字符。然后,您刚刚重新发明了C风格的字符串。 - Cody Gray
@CodyGray - 技术上是正确的(最好的正确方式)!我的观点是char *不一定是一个c字符串,在这些情况下需要重载,而在后一种情况下需要一个长度参数。 - Karl Nicoll

0

嗯,是的,假设你想从构造函数中释放char *字符串所占用的内存。在这种情况下,你需要两个不同的构造函数。


0

如果您正在使用需要(const) char*函数参数的C API,通常(也)需要提供(const) char* C风格接口。

例如,考虑一个RAII包装器Iconv用于iconv(3)。在Iconv的初始化中,您将需要进行包装。

iconv_t iconv_open(const char* tocode, const char* fromcode);

你真的想让 Iconv 的(仅有的)构造函数是这样吗?
Iconv(const std::string& tocode, const std::string& fromcode);

特别是如果您的参数(tocode,fromcode)很可能来自命令行(argv)?

在这种情况下,我会定义相应的C风格构造函数,可能还会为方便起见定义前一个构造函数(然后通过std :: string :: c_str()调用C风格构造函数)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接