为什么C++程序员要尽量减少使用“新”?

在使用std::list<时,我偶然发现了堆栈溢出std::string的问题内存泄漏;标准::字符串>,其中一条评论说:

停止过多地使用new。我看不出你为什么在任何地方使用新的
是的。您可以在C++中按值创建对象,这是
使用该语言的巨大优势。
您不必分配
堆上的所有东西。
不要像Java程序员那样思考

我不太清楚他那是什么意思

为什么要在C++中尽可能多地使用值创建对象,它在内部有什么区别?
我误解了答案吗

有两种广泛使用的内存分配技术:自动分配和动态分配。通常,每个内存都有一个对应的区域:堆栈和堆

堆叠

堆栈总是按顺序分配内存。它可以这样做,因为它要求您以相反的顺序释放内存(先进先出:FILO)。这是许多编程语言中局部变量的内存分配技术。它非常非常快,因为它需要最少的簿记,并且下一个要分配的地址是隐式的

在C++中,这称为EM>自动存储,因为存储在范围结束时自动声明。当前代码块(使用{}分隔)的执行完成后,将自动收集该块中所有变量的内存。这也是调用析构函数来清理资源的时刻

堆允许更灵活的内存分配模式。记账更复杂,分配更慢。由于没有隐式释放点,因此必须使用deletedelete[]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实例、四个实例、一个实例和所有字符串的内容,所有内容都自动释放

发表评论