这两者之间有一个重要的区别。
所有没有使用new分配的对象的行为都很像c#中的值类型(人们经常说这些对象分配在堆栈上,这可能是最常见/最明显的情况,但并不总是正确的)。更准确地说,不使用new分配的对象具有自动存储持续时间
用new分配的所有东西都在堆上分配,并返回指向它的指针,就像c#中的引用类型一样。
Anything allocated on the stack has to have a constant size, determined at compile-time (the compiler has to set the stack pointer correctly, or if the object is a member of another class, it has to adjust the size of that other class). That's why arrays in C# are reference types. They have to be, because with reference types, we can decide at runtime how much memory to ask for. And the same applies here. Only arrays with constant size (a size that can be determined at compile-time) can be allocated with automatic storage duration (on the stack). Dynamically sized arrays have to be allocated on the heap, by calling new.
(这就是与c#的相似之处)
现在,在堆栈上分配的任何东西都具有“自动”存储持续时间(实际上,您可以将变量声明为auto,但如果没有指定其他存储类型,这是默认的,因此在实践中并不真正使用关键字,但这就是它的来源)
自动存储持续时间就像它听起来一样,变量的持续时间是自动处理的。相比之下,在堆上分配的任何内容都必须由您手动删除。
这里有一个例子:
void foo() {
bar b;
bar* b2 = new bar();
}
这个函数创建了三个值得考虑的值:
在第1行,它在堆栈上声明了一个类型为bar的变量b(自动持续时间)。
在第2行,它在堆栈上声明了一个bar指针b2(自动持续时间),并调用new,在堆上分配一个bar对象。(动态时间)
When the function returns, the following will happen:
First, b2 goes out of scope (order of destruction is always opposite of order of construction). But b2 is just a pointer, so nothing happens, the memory it occupies is simply freed. And importantly, the memory it points to (the bar instance on the heap) is NOT touched. Only the pointer is freed, because only the pointer had automatic duration.
Second, b goes out of scope, so since it has automatic duration, its destructor is called, and the memory is freed.
那堆上的酒吧侍者呢?它可能还在那里。没人删除它,所以我们泄露了内存。
From this example, we can see that anything with automatic duration is guaranteed to have its destructor called when it goes out of scope. That's useful. But anything allocated on the heap lasts as long as we need it to, and can be dynamically sized, as in the case of arrays. That is also useful. We can use that to manage our memory allocations. What if the Foo class allocated some memory on the heap in its constructor, and deleted that memory in its destructor. Then we could get the best of both worlds, safe memory allocations that are guaranteed to be freed again, but without the limitations of forcing everything to be on the stack.
这就是大多数c++代码的工作原理。
例如,看看标准库的std::vector。它通常在堆栈上分配,但可以动态地调整大小和大小。它通过在必要时在堆上内部分配内存来实现这一点。类的用户永远不会看到这一点,所以不会有泄漏内存的机会,也不会忘记清理分配的内存。
这个原则被称为RAII(资源获取即初始化),它可以扩展到任何必须获取和释放的资源。(网络套接字,文件,数据库连接,同步锁)。所有这些资源都可以在构造函数中获取,并在析构函数中释放,因此可以保证获得的所有资源都将再次被释放。
作为一般规则,永远不要直接从高级代码中使用new/delete。始终将它包装在一个可以为您管理内存的类中,并确保它再次被释放。(是的,这条规则可能有例外。特别是,智能指针要求您直接调用new,并将指针传递给它的构造函数,然后由构造函数接管并确保正确地调用delete。但这仍然是一个非常重要的经验法则)