有两种广泛使用的内存分配技术:自动分配和动态分配。通常,每个内存区域都有一个相应的区域:堆栈和堆。
Stack
堆栈总是按顺序分配内存。它可以这样做,因为它要求您以相反的顺序释放内存(先进后出:FILO)。这是许多编程语言中局部变量的内存分配技术。它非常非常快,因为它需要最少的簿记,并且下一个要分配的地址是隐式的。
在 C++ 中,这称为自动存储因为存储空间会在范围结束时自动声明。一旦执行当前代码块(使用分隔{}
)完成后,该块中所有变量的内存都会自动收集。这也是此时此刻析构函数被调用来清理资源。
Heap
堆允许更灵活的内存分配模式。记账更复杂,分配也更慢。因为没有隐式释放点,所以必须手动释放内存,使用delete
or delete[]
(free
在C)中。然而,没有隐式释放点是堆灵活性的关键。
使用动态分配的原因
即使使用堆速度较慢并且可能导致内存泄漏或内存碎片,动态分配也有非常好的用例,因为它的限制较少。
使用动态分配的两个主要原因:
为什么动态分配通常是不必要的
在 C++ 中,有一个简洁的构造,称为析构函数。此机制允许您通过将资源的生命周期与变量的生命周期对齐来管理资源。这种技术称为RAII这是C++的区别点。它将资源“包装”成对象。std::string
就是一个完美的例子。这个片段:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
实际上分配了可变数量的内存。这std::string
对象使用堆分配内存并在其析构函数中释放它。在这种情况下,你做了not不需要手动管理任何资源,仍然可以获得动态内存分配的好处。
特别是,在这个片段中它意味着:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
存在不需要的动态内存分配。该程序需要更多的输入(!)并带来忘记释放内存的风险。它这样做并没有明显的好处。
为什么应该尽可能经常使用自动存储
基本上,最后一段总结了这一点。尽可能经常使用自动存储可以使您的程序:
- 打字速度更快;
- 跑步时速度更快;
- 不太容易出现内存/资源泄漏。
奖励积分
在引用的问题中,还有其他问题。特别是下面的类:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
实际上使用起来比下面的风险要大得多:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
原因是std::string
正确定义复制构造函数。考虑以下程序:
int main ()
{
Line l1;
Line l2 = l1;
}
使用原始版本,该程序可能会崩溃,因为它使用delete
在同一根弦上两次。使用修改后的版本,每个Line
实例将拥有自己的字符串instance,每个都有自己的内存,并且都将在程序结束时释放。
其他注意事项
广泛使用RAII由于上述所有原因,被认为是 C++ 中的最佳实践。然而,还有一个并非立即显而易见的额外好处。基本上,它比各个部分的总和更好。整个机制composes。它可以扩展。
如果您使用Line
类作为构建块:
class Table
{
Line borders[4];
};
Then
int main ()
{
Table table;
}
分配四个std::string
实例,四个Line
实例,一Table
实例和所有字符串的内容以及一切都会自动释放.