There are two widely-used memory allocation techniques: automatic allocation and dynamic allocation.
(有两种广泛使用的内存分配技术:自动分配和动态分配。)
Commonly, there is a corresponding region of memory for each: the stack and the heap. (通常,每个都有一个对应的内存区域:堆栈和堆。)
Stack (堆)
The stack always allocates memory in a sequential fashion.
(堆栈始终以顺序方式分配内存。)
It can do so because it requires you to release the memory in the reverse order (First-In, Last-Out: FILO). (这样做是因为它要求您以相反的顺序释放内存(先进先出:FILO)。)
This is the memory allocation technique for local variables in many programming languages. (这是许多编程语言中用于局部变量的内存分配技术。)
It is very, very fast because it requires minimal bookkeeping and the next address to allocate is implicit. (这是非常非常快的,因为它需要最少的簿记并且下一个要分配的地址是隐式的。)
In C++, this is called automatic storage because the storage is claimed automatically at the end of scope.
(在C ++中,这称为自动存储,因为该存储在作用域末尾自动声明。)
As soon as execution of current code block (delimited using {}
) is completed, memory for all variables in that block is automatically collected. (当前代码块(使用{}
分隔)的执行完成后,将自动收集该块中所有变量的内存。)
This is also the moment where destructors are invoked to clean up resources. (这也是调用析构函数清理资源的时刻。)
Heap (堆)
The heap allows for a more flexible memory allocation mode.
(堆允许更灵活的内存分配模式。)
Bookkeeping is more complex and allocation is slower. (簿记更加复杂,分配也较慢。)
Because there is no implicit release point, you must release the memory manually, using delete
or delete[]
( free
in C). (因为没有隐式的释放点,所以必须使用delete
或delete[]
(在C语言中free
)手动释放内存。)
However, the absence of an implicit release point is the key to the heap's flexibility. (但是,缺少隐式释放点是堆灵活性的关键。)
Reasons to use dynamic allocation (使用动态分配的原因)
Even if using the heap is slower and potentially leads to memory leaks or memory fragmentation, there are perfectly good use cases for dynamic allocation, as it's less limited.
(即使使用堆的速度较慢,并可能导致内存泄漏或内存碎片,但由于动态内存分配的局限性较小,因此存在很好的用例。)
Two key reasons to use dynamic allocation:
(使用动态分配的两个主要原因:)
You don't know how much memory you need at compile time.
(您不知道在编译时需要多少内存。)
For instance, when reading a text file into a string, you usually don't know what size the file has, so you can't decide how much memory to allocate until you run the program. (例如,在将文本文件读取为字符串时,通常不知道文件的大小,因此在运行程序之前,您无法确定要分配多少内存。)
You want to allocate memory which will persist after leaving the current block.
(您想要分配在离开当前块后仍将保留的内存。)
For instance, you may want to write a function string readfile(string path)
that returns the contents of a file. (例如,您可能想编写一个函数string readfile(string path)
,该函数返回文件的内容。)
In this case, even if the stack could hold the entire file contents, you could not return from a function and keep the allocated memory block. (在这种情况下,即使堆栈可以容纳整个文件内容,也无法从函数返回并保留分配的内存块。)
Why dynamic allocation is often unnecessary (为什么通常不需要动态分配)
In C++ there's a neat construct called a destructor .
(在C ++中,有一个名为destructor的简洁构造。)
This mechanism allows you to manage resources by aligning the lifetime of the resource with the lifetime of a variable. (此机制使您可以通过将资源的生存期与变量的生存期对齐来管理资源。)
This technique is called RAII and is the distinguishing point of C++. (该技术称为RAII ,是C ++的区别点。)
It "wraps" resources into objects. (它将资源“包装”为对象。)
std::string
is a perfect example. (std::string
是一个很好的例子。)
This snippet: (此代码段:)
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
actually allocates a variable amount of memory.
(实际上分配了可变数量的内存。)
The std::string
object allocates memory using the heap and releases it in its destructor. (std::string
对象使用堆分配内存,并在其析构函数中释放它。)
In this case, you did not need to manually manage any resources and still got the benefits of dynamic memory allocation. (在这种情况下,你不需要手动管理的任何资源,还是把动态内存分配的好处。)
In particular, it implies that in this snippet:
(特别是,它暗示在此代码段中:)
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
there is unneeded dynamic memory allocation.
(不需要动态内存分配。)
The program requires more typing (!) and introduces the risk of forgetting to deallocate the memory. (该程序需要更多的键入(!),并带来了忘记重新分配内存的风险。)
It does this with no apparent benefit. (它这样做没有明显的好处。)
Why you should use automatic storage as often as possible (为什么您应该尽可能频繁地使用自动存储)
Basically, the last paragraph sums it up.
(基本上,最后一段进行了总结。)
Using automatic storage as often as possible makes your programs: (尽可能频繁地使用自动存储可以使您的程序:)
- faster to type;
(打字速度更快;)
- faster when run;
(运行时更快)
- less prone to memory/resource leaks.
(不太容易发生内存/资源泄漏。)
Bonus points (奖励积分)
In the referenced question, there are additional concerns.
(在提到的问题中,还有其他问题。)
In particular, the following class: (特别是以下类:)
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
Is actually a lot more risky to use than the following one:
(实际上,使用风险要高于以下风险:)
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
The reason is that std::string
properly defines a copy constructor.
(原因是std::string
正确定义了副本构造函数。)
Consider the following program: (考虑以下程序:)
int main ()
{
Line l1;
Line l2 = l1;
}
Using the original version, this program will likely crash, as it uses delete
on the same string twice.
(使用原始版本,此程序可能会崩溃,因为它在同一字符串上两次使用了delete
。)
Using the modified version, each Line
instance will own its own string instance , each with its own memory and both will be released at the end of the program. (使用修改后的版本,每个Line
实例将拥有自己的字符串实例 ,每个都有自己的内存,并且两者都将在程序结尾处释放。)
Other notes (其他注意事项)
Extensive use of RAII is considered a best practice in C++ because of all the reasons above.
(由于上述所有原因,广泛使用RAII被认为是C ++中的最佳实践。)
However, there is an additional benefit which is not immediately obvious. (但是,还有其他好处尚不明显。)
Basically, it's better than the sum of its parts. (基本上,它比各个部分的总和要好。)
The whole mechanism composes . (整个机制组成 。)
It scales. (它可以缩放。)
If you use the Line
class as a building block:
(如果将Line
类用作构建基块:)
class Table
{
Line borders[4];
};
Then
(然后)
int main ()
{
Table table;
}
allocates four std::string
instances, four Line
instances, one Table
instance and all the string's contents and everything is freed automagically .
(分配四个std::string
实例,四个Line
实例,一个Table
实例以及所有字符串的内容,并且所有内容都会自动释放 。)