在使用std::list<;时,我偶然发现了堆栈溢出std::string的问题内存泄漏;标准::字符串>;,其中一条评论说:
停止过多地使用
new。我看不出你为什么在任何地方使用新的
是的。您可以在C++中按值创建对象,这是
使用该语言的巨大优势。
您不必分配
堆上的所有东西。
不要像Java程序员那样思考
我不太清楚他那是什么意思
为什么要在C++中尽可能多地使用值创建对象,它在内部有什么区别?
我误解了答案吗
有两种广泛使用的内存分配技术:自动分配和动态分配。通常,每个内存都有一个对应的区域:堆栈和堆
堆叠
堆栈总是按顺序分配内存。它可以这样做,因为它要求您以相反的顺序释放内存(先进先出:FILO)。这是许多编程语言中局部变量的内存分配技术。它非常非常快,因为它需要最少的簿记,并且下一个要分配的地址是隐式的
在C++中,这称为EM>自动存储,因为存储在范围结束时自动声明。当前代码块(使用{}分隔)的执行完成后,将自动收集该块中所有变量的内存。这也是调用析构函数来清理资源的时刻
堆
堆允许更灵活的内存分配模式。记账更复杂,分配更慢。由于没有隐式释放点,因此必须使用delete或delete[](free在C中)手动释放内存。然而,缺少隐式释放点是堆灵活性的关键
使用动态分配的原因
即使使用堆速度较慢,并且可能导致内存泄漏或内存碎片,动态分配也有非常好的用例,因为它的限制较少
使用动态分配的两个关键原因:
-
您不知道编译时需要多少内存。例如,当将文本文件读入字符串时,您通常不知道文件的大小,因此在运行程序之前无法决定分配多少内存
-
您希望分配在离开当前块后将保持的内存。例如,您可能希望编写一个函数
string readfile(string path),返回文件的内容。在这种情况下,即使堆栈可以保存整个文件内容,也无法从函数返回并保留分配的内存块
为什么动态分配常常是不必要的
在C++中有一个简洁的构造,称为析构函数。此机制允许您通过将资源的生存期与变量的生存期对齐来管理资源。这种技术被称为RAII,是C++的一个识别点。它”;“包装”;将资源转换为对象标准::字符串就是一个完美的例子。这个片段:
intmain(intargc,char*argv[]
{
std::字符串程序(argv[0]);
}
实际上分配了可变的内存量。std::string对象使用堆分配内存,并在其析构函数中释放内存。在这种情况下,您不需要手动管理任何资源,但仍然可以获得动态内存分配的好处
特别是,这意味着在这个片段中:
intmain(intargc,char*argv[]
{
std::string*program=new std::string(argv[0]);//糟糕!
删除程序;
}
存在不必要的动态内存分配。该程序需要更多的输入(!),并引入了忘记释放内存的风险。它这样做没有明显的好处
为什么要尽可能多地使用自动存储
基本上,最后一段总结了这一点。尽可能多地使用自动存储可以使您的程序:
- 打字更快
- 跑得更快
- 不易发生内存/资源泄漏
加分
在提到的问题中,还有其他问题。特别是以下类别:
类行{
公众:
第()行;
~Line();
std::string*mString;
};
行::行(){
mString=new std::string(“foo_bar”);
}
行::~Line(){
删除mString;
}
实际上,使用的风险比下面的要大得多:
类行{
公众:
第()行;
std::字符串mString;
};
行::行(){
mString=“foo”bar“;;
//注意:有一种更简洁的方法来写这个。
}
原因是std::string正确地定义了复制构造函数。考虑下面的程序:
int main()
{
l1线;
直线l2=l1;
}
使用原始版本,此程序可能会崩溃,因为它对同一字符串使用了两次delete。使用修改后的版本,每个行实例将拥有自己的字符串实例,每个实例都有自己的内存,并且都将在程序结束时释放
其他注释
由于以上种种原因,RAI的广泛使用被认为是C++的最佳实践。然而,还有一个额外的好处不是很明显。基本上,它比它各部分的总和要好。整个机构由组成。它的规模
如果使用行类作为构建块:
类表
{
线边界[4];
};
然后
int main()
{
表格;
}
分配四个std::string实例、四个行实例、一个表实例和所有字符串的内容,所有内容都自动释放