C++字符串对字符串文字不够优化

问题描述:

在我的一个C++项目中,我是用std::string替换所有char*之前的一个步骤,但我发现std::string失败的一个特定情况。C++字符串对字符串文字不够优化

想象我有这2个功能:

void foo1(const std::string& s) 
{ 
    ... 
} 

void foo2(const char* s) 
{ 
    ... 
} 

如果我写的是这样的:

const char* SL = "Hello to all!"; 

foo1(SL); // calls malloc, memcpy, free 
foo2(SL); 

foo1SL将隐式转换为std::string。这意味着std::string构造函数将分配内存,并且它会将字符串文字复制到该缓冲区。在foo2虽然没有这些会发生。

在大多数实现中,std::string应该是超级优化的(例如写入时拷贝),但是当我使用const char*构建时,它不是。我的问题是:为什么会发生这种情况?我错过了什么吗?我的标准库没有足够优化或者出于某种原因(我不知道),这是完全不安全的吗?

+4

复制写入并不是真正的“超级优化”。我相信海湾合作委员会的标准库仍然使用它,但仅仅是因为十年前,在多线程是常态之前它才有意义。今天制定的一个理智的图书馆实施将避免像瘟疫一样的COW。 – jalf

+2

我认为该标准甚至不允许COW,因为成员函数的迭代器失效要求。 – Xeo

+4

Afaik C++ 03允许COW。我相信C++ 11禁止它 – jalf

的问题是,有没有办法让std :: string类识别const char*指针是否是一个全球性的文字或不:

const char *a = "Hello World"; 
const char *b = new char[20]; 

的字符*指针可能会在任何时候无效(例如,当它是本地变量并且函数/作用域结束时),因此std::string必须成为字符串的独占所有者。这只能通过复制来实现。

下面的例子说明为什么它是必要的:

std::string getHelloWorld() { 
    char *hello = new char[64]; 
    strcpy(hello, "Hello World"); 
    std::string result = (const char *)hello; // If std::string didn't make a copy, the result could be a garbage 
    delete[] hello; 
    return result; 
} 
+0

实际上,一个字符串文字是一个'char [N]',其中N是长度+ 1(空终止符)。 – Xeo

+2

为什么“新”?在堆栈上分配缓冲区也可以:'char const hello [] =“Hello World”;' –

+1

@MatthieuM .:你的版本是异常安全的,而dark_charlie不是字符串构造函数不是no-扔。 –

std::string不是银弹。它的目的是尽可能实现一个通用可变字符串,它拥有自己的内存,并且使用C API非常便宜。这些都是常见的情况,但它们不匹配字符串用法的实例。

正如你所说的,字符串文字不适合这个用例。他们使用静态分配的内存,因此std::string不能也不应该尝试获取内存的所有权。而这些字符串是总是只读,所以std::string不能让你修改它们。

std::string创建传递给它的字符串数据的副本,然后在内部对此副本进行操作。

如果你想在常量字符串,其寿命是其他地方处理(在字符串的情况下,它是由它初始化并释放静态数据的运行时库处理)进行操作,那么你可能想使用一个不同的字符串表示。也许只是一个简单的const char*

+0

*这是拥有它的内存的可变字符串的最佳实现*不。差远了。 '.c_str()'所要求的严格要求成本。当字符串很大并且被修改时,最好的实现可能会使用B-树来避免所有那些昂贵的重新分配。 –

+0

但它会失去连续性,这将使转换为C字符串更昂贵。有很多折衷需要考虑。 :)但我澄清了我的答案了一下。 – jalf

+0

是的,这就是为什么我提到'c_str'。我相信SGI STL有一个“绳索”类来覆盖不需要C交互的情况。 –

其实,你的烦恼会消失(*)如果你改变了文字:

std::string const SL = "Hello to all!"; 

我加入了const你。

foo1(SL);   // by const-reference, exact same cost than a pointer 
foo2(SL.c_str()); // simple pointer 

如果你想要移动到std::string,不仅切换功能:现在

,呼吁foo1将不涉及任何复制(所有),并调用foo2可以以很小的成本实现接口,也切换变量(和常量)。 (*)原始答案假定SL是一个全局常量,如果它是一个函数的局部变量,那么如果真的希望在每次调用时避免构建它,那么它可以被制作为static

+0

另外一点:如果字符串文字在某个函数中,则可能需要将其设为静态。 –

+4

这并不意味着所有的字符串文字都会在启动时被复制到堆中吗? –

+0

为什么忧虑会消失?现在,std :: string对象将在输入/退出范围内构造/析构,这可能会导致与之前相同的内存分配/释放(取决于std :: string实现)。使其成为const不会使其成为静态的,是吗? –