为什么 C++ 程序员应该尽量减少“new"的使用? [英] Why should C++ programmers minimize use of 'new'?

查看:39
本文介绍了为什么 C++ 程序员应该尽量减少“new"的使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我偶然发现了堆栈溢出问题使用 std::list 时 std::string 的内存泄漏<std::string>其中一条评论是这样说的:

I stumbled upon Stack Overflow question Memory leak with std::string when using std::list<std::string>, and one of the comments says this:

停止使用 new 这么多.我看不出你在任何地方使用 new 的任何理由你做到了.您可以在 C++ 中按值创建对象,它是使用该语言的巨大优势.
您不必分配堆上的一切.
不要像 Java 程序员那样思考.

Stop using new so much. I can't see any reason you used new anywhere you did. You can create objects by value in C++ and it's one of the huge advantages to using the language.
You do not have to allocate everything on the heap.
Stop thinking like a Java programmer.

我不太确定他的意思是什么.

I'm not really sure what he means by that.

为什么在 C++ 中应该尽可能频繁地按值创建对象,它在内部有什么区别?
我误解了答案吗?

Why should objects be created by value in C++ as often as possible, and what difference does it make internally?
Did I misinterpret the answer?

推荐答案

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

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.

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

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). 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.

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

In C++, this is called automatic storage because the storage is claimed automatically at the end of scope. 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.

堆允许更灵活的内存分配模式.簿记更复杂,分配更慢.由于没有隐式释放点,您必须手动释放内存,使用deletedelete[](C 中的free).然而,没有隐式释放点是堆灵活性的关键.

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). However, the absence of an implicit release point is the key to the heap's flexibility.

即使使用堆较慢并且可能导致内存泄漏或内存碎片,动态分配也有非常好的用例,因为它的限制较少.

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.

你想要分配的内存在离开当前块后仍然存在.例如,您可能想要编写一个函数 string readfile(string path) 来返回文件的内容.在这种情况下,即使堆栈可以容纳整个文件内容,您也无法从函数返回并保留分配的内存块.

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. 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.

在 C++ 中有一个简洁的构造,称为 析构函数.这种机制允许您通过将资源的生命周期与变量的生命周期对齐来管理资源.这种技术称为RAII,是C++的区别点.它包裹"资源转化为对象.std::string 就是一个完美的例子.这段代码:

In C++ there's a neat construct called a 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++. It "wraps" resources into objects. std::string is a perfect example. This snippet:

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

实际上分配了可变数量的内存.std::string 对象使用堆分配内存并在其析构函数中释放它.在这种情况下,您不需要手动管理任何资源,仍然可以享受动态内存分配的好处.

actually allocates a variable amount of memory. The std::string object allocates memory using the heap and releases it in its destructor. 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.

基本上,最后一段总结了它.尽可能多地使用自动存储使您的程序:

Basically, the last paragraph sums it up. Using automatic storage as often as possible makes your programs:

  • 打字速度更快;
  • 运行速度更快;
  • 不太容易发生内存/资源泄漏.

在引用的问题中,还有其他问题.特别是以下类:

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.
}

原因是 std::string 正确定义了一个复制构造函数.考虑以下程序:

The reason is that std::string properly defines a copy constructor. Consider the following program:

int main ()
{
    Line l1;
    Line l2 = l1;
}

使用原始版本,这个程序可能会崩溃,因为它在同一个字符串上使用了两次 delete.使用修改后的版本,每个Line 实例将拥有自己的字符串instance,每个实例都有自己的内存,并且都将在程序结束时释放.

Using the original version, this program will likely crash, as it uses delete on the same string twice. 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.

广泛使用 RAII 被认为是 C++ 中的最佳实践.然而,还有一个额外的好处并不是立即显而易见的.基本上,它比其各部分的总和要好.整个机制组成.它可以缩放.

Extensive use of RAII is considered a best practice in C++ because of all the reasons above. 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.

如果您使用 Line 类作为构建块:

If you use the Line class as a building block:

 class Table
 {
      Line borders[4];
 };

然后

 int main ()
 {
     Table table;
 }

分配四个 std::string 实例,四个 Line 实例,一个 Table 实例和所有字符串的内容,一切都是自动释放.

allocates four std::string instances, four Line instances, one Table instance and all the string's contents and everything is freed automagically.

这篇关于为什么 C++ 程序员应该尽量减少“new"的使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆