如何正确释放 std::string 的内存

60

当我不再需要使用一个在堆上分配的std::string时,最好的删除方式是什么?谢谢!


1
@Ryan Mitchell:1)这个问题可以通过阅读大多数C++书籍的前两章轻松回答,2)有成千上万个完全重复的问题,3)表述不清楚,不清楚OP想要问什么。 - Lie Ryan
3
首先,是的,我对 C++ 相当新,并且我没有使用 C++ 书籍。我通过参考 MSDN 文库来学习 C++,但是我无法找到有关在堆上分配的字符串如何释放的具体信息,而且我不确定使用 DELETE 函数是否是一个好主意,或者我是否应该使用 free(&str) 或其他一些函数。我能理解为什么它可能会受到负评,但我之前已经尝试过 Google 和 MSDN,但找不到很好的答案,所以我决定在这里发帖,最终得到了我要找的答案。 - bbosak
2
@IDWMaster:C++不是一种通过到处阅读参考文档来学习的语言,我强烈建议您获取一本C++书籍并学习它。 - Matteo Italia
1
@IDWMaster:C++在语法上与C和C#相似,但在习惯用法上不同。 - Matteo Italia
3
IDWMaster: 获取一本书。如果我说“我可以飞这架飞机,毕竟我会开车!它们都有轮子。”你可能会笑。你的C#或C到C++的知识也同样适用。你不应该假装不同编程语言之间有1-1的功能映射关系来学习编程语言,它们之间存在差异是有原因的。像个新手一样去学习,不要自以为是。作为一个初学者,你没有任何权威来断言自己知道正在做什么;事实上你并不知道。听从我们的建议。 - GManNickG
显示剩余9条评论
8个回答

89

std::string只是一个普通的类1,因此通常的规则也适用。

如果你在栈上、全局变量或类成员中分配std::string对象,你不需要进行任何特别的操作。当它们超出作用域时,析构函数会被调用,并自动释放用于字符串的内存。

int MyUselessFunction()
{
    std::string mystring="Just a string.";
    // ...
    return 42;
    // no need to do anything, mystring goes out of scope and everything is cleaned up automatically
}

你只有在使用 new 运算符在堆上分配一个std::string对象时才需要处理;在这种情况下,就像使用 new 分配的任何对象一样,你必须调用delete释放它。

int MyUselessFunction()
{
    // for some reason you feel the need to allocate that string on the heap
    std::string * mystring= new std::string("Just a string.");
    // ...
    // deallocate it - notice that in the real world you'd use a smart pointer
    delete mystring;
    return 42;
}

正如这个例子所暗示的,通常情况下在堆上分配std::string是没有意义的,即使需要这样做,仍应该将指针封装在智能指针中,以避免甚至出现内存泄漏的风险(例如在异常情况、多个返回路径等情况下)。


  1. 实际上,std::string被定义为

    namespace std
    {
        typedef std::basic_string<char> string;
    };
    

    所以它是basic_string模板类用于字符类型char的实例化的同义词(这不会改变答案,但在Stack Overflow上,即使在新手问题上,你也必须很严谨)。


1
@Sergey Tachenov:std::basic_string<char>是一种模板特化,typedef为其创建了一个同义词;既然他说他懂C,我认为typedef的作用应该很明显;不过还是进行了修正,下次请随意进行编辑。 - Matteo Italia
@Matteo,看起来我在这里也是个新手。我一直认为“专业化”只指使用template<>语法的显式专业化或部分显式专业化。结果证明并非如此,对于误导性评论我感到抱歉。 - Sergei Tachenov
@Matteo,在发布上一条评论之前,我找到了这个定义:“从模板声明和一个或多个模板参数创建函数、类或类成员的新定义的行为称为模板实例化。从模板实例化创建的定义称为特化。”听起来很合理。 - Sergei Tachenov
如果我在全局范围内定义字符串 string aaa,然后在某个时刻将其设置为某个值 aaa = string("长字符串,例如文件内容......")。如果我再次进行赋值操作会发生什么?(即再次执行 aaa = string("另一个长字符串")?第一个字符串是否会自动从堆中释放? - Mike Keskinov
1
@MikeKeskinov:是的;两个赋值都会创建一个临时字符串,该字符串被移动分配给aaa;在赋值时,aaa的先前内容会自动释放,而(现在为空的)临时字符串将在表达式结束时销毁。请注意,不涉及临时变量的赋值行为略有不同(目标字符串的存储被回收以容纳新内容,而不是被释放,因此即使大于必要的容量,它也将保留现有容量),但您永远不会冒任何内存泄漏的风险:当std::string被销毁时,其内存会被释放。 - Matteo Italia
显示剩余3条评论

12
std::string foo("since it's on the stack, it will auto delete out of scope");

或:

std::string* foo = new std::string("allocated on the heap needs explicit destruction")
delete foo;

4

如果变量在堆上,使用delete释放;如果变量在栈上,则无需释放。


2
如果您想清除内部std::string的分配,您需要以下内容:
std::string str;

// fill str with 250 chars

str.clear(); // clears the content: size = 0, capacity = 250
str.shrink_to_fit(); // shrinks the capacity: size = 0, capacity = 16 or 22 (depends on implementation) 

1
void foo() {
    string* myString = new string("heap-allocated objects are deleted on 'delete myString;'");
    cout << *myString << endl;
    delete myString;
}

或者更好的方法是尽可能避免使用指针,而是使用自动变量:

void foo() {
    string myString("stack-allocated string is automatically deleted when myString goes out of scope");
    cout << myString << endl;
}

1

也许你正在处理释放内部字符串缓冲区的问题?

出于性能原因,大多数实现都会保留内部缓冲区的分配,即使字符串已经“清空”。此外:小字符串(小于sizeof(ptr))直接存储在保存指针的区域中。这些字节在字符串的生命周期内永远无法被回收。

要释放内部资源:经典的技巧是在作用域内使用swap。这将强制缓冲区真正释放(也适用于vector/map/ostream/stringstream等...):

string s;                                               // size==0 and capacity==15 as the default proxy of the container (help perf on small string)
s = "Looooooooooooooooooooooooooooooong String";        // size==41 and capacity==47 bytes allocated
s.clear();                                              // size==0  BUT capacity==47 bytes STILL allocated!!
        
s = "Looooooooooooooooooooooooooooooong String";        // size==41 and capacity reuse 47 bytes still allocated. 
s.resize(0);                                            // size==0  BUT capacity==47 bytes STILL allocated!!
// swap with scope to force freeing string internals
{ 
    string o;
    o.swap(s);
}                                                       // size==0 AND capacity==15 (back to smallest footprint possible)
s = "12345";                                            // size==5 AND capacity==15 (the string is IN the container, no new alloc)

1

只需将std :: string视为任何基本类型即可。

std::string *str = new std::string("whatever");
///code
delete str;

-1

您可以像对待其他类一样处理std::string。使用new进行分配,完成后使用delete释放。 在C++11中,我不建议在大多数情况下使用newdelete。如果需要在堆上分配字符串,请使用std::shared_ptr进行包装:

std::shared_ptr<std::string> my_string = std::make_shared<std::string>(std::string("My string"));

一旦所有my_string的副本超出范围,相关内存将自动删除。


1
std::shared_ptr 的存在是为了提供优雅的代理共享所有权语义和自动引用计数,而不是为了使堆分配更容易。然而,std::unique_ptr 是完全不同的故事。在建议使用任何一个之前,提到使用两者的影响可能是明智的... - Mateusz Grzejek

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