我一直在使用C++,我一直在想新的关键字.简单地说,我应该使用它吗?
1)使用新关键字...
MyClass* myClass = new MyClass(); myClass->MyField = "Hello world!";
2)没有新的关键字......
MyClass myClass; myClass.MyField = "Hello world!";
从实现的角度来看,它们看起来并没有什么不同(但我确定它们是......)但是,我的主要语言是C#,当然第一种方法就是我习惯的方法.
困难似乎是方法1更难用于std C++类.
我应该使用哪种方法?
我最近使用new关键字作为堆内存(或免费存储)用于超出范围的大型数组(即从函数返回).在我使用堆栈之前,导致一半的元素在范围之外被破坏,切换到堆使用确保元素是完整的.好极了!
我的一位朋友最近告诉我,使用new
关键字有一个简单的规则; 每次打字new
,输入delete
.
Foobar *foobar = new Foobar(); delete foobar; // TODO: Move this to the right place.
这有助于防止内存泄漏,因为您总是必须将删除放在某处(即,当您将其剪切并粘贴到析构函数或其他方式时).
方法1(使用new
)
为免费商店上的对象分配内存 (这通常与堆相同)
需要您delete
稍后明确您的对象.(如果不删除它,可能会造成内存泄漏)
记忆保持分配直到你delete
.(即你可以return
使用你创建的对象new
)
除非指针是d,否则问题中的示例将泄漏内存delete
; 并且应该始终删除它,无论采用哪个控制路径,或者抛出异常.
方法2(不使用new
)
为堆栈中的对象分配内存(所有局部变量都去了)堆栈的内存通常较少; 如果分配了太多对象,则存在堆栈溢出的风险.
你delete
以后不需要它.
当超出范围时,不再分配内存.(即你不应该return
指向堆栈上的对象)
至于哪一个使用; 鉴于上述限制,您可以选择最适合您的方法.
一些简单的案例:
如果您不想担心调用delete
(以及可能导致内存泄漏),则不应使用new
.
如果您想从函数返回指向对象的指针,则必须使用 new
两者之间存在重要差异.
未分配的所有内容都与new
C#中的值类型非常相似(并且人们经常说这些对象是在堆栈上分配的,这可能是最常见/最明显的情况,但并非总是如此.更确切地说,未使用时分配的对象new
具有自动存储duration
分配的所有内容都new
在堆上分配,并返回指向它的指针,就像C#中的引用类型一样.
在堆栈上分配的任何东西都必须具有一个常量大小,在编译时确定(编译器必须正确设置堆栈指针,或者如果对象是另一个类的成员,则必须调整其他类的大小) .这就是为什么C#中的数组是引用类型的原因.它们必须是,因为使用引用类型,我们可以在运行时决定要求多少内存.这同样适用于此.只有具有常量大小(可在编译时确定的大小)的数组才能分配自动存储持续时间(在堆栈上).必须通过调用在堆上分配动态大小的数组new
.
(那就是与C#有任何相似之处的地方)
现在,在堆栈上分配的任何内容都具有"自动"存储持续时间(您实际上可以将变量声明为auto
,但如果没有指定其他存储类型,则这是默认值,因此关键字在实践中并未真正使用,但这是它的位置来自)
自动存储持续时间意味着它的确切含义,变量的持续时间是自动处理的.相比之下,堆上分配的任何内容都必须由您手动删除.这是一个例子:
void foo() { bar b; bar* b2 = new bar(); }
此函数创建三个值得考虑的值:
在第1行,它在堆栈上声明了一个b
类型的变量bar
(自动持续时间).
在第2行,它在堆栈上声明一个bar
指针b2
(自动持续时间),并调用new,bar
在堆上分配一个对象.(动态持续时间)
当函数返回时,会发生以下情况:首先,b2
超出范围(破坏的顺序始终与构造顺序相反).但b2
它只是一个指针,所以没有任何反应,它占用的内存只是被释放.而且重要的是,它所指向的内存(bar
堆上的实例)不会被触及.仅释放指针,因为只有指针具有自动持续时间.其次,b
超出范围,因为它具有自动持续时间,所以调用其析构函数,并释放内存.
和bar
堆上的实例?它可能还在那里.没有人打扰删除它,所以我们泄露了记忆.
从这个例子中,我们可以看到任何具有自动持续时间的东西都保证在它超出范围时调用它的析构函数.那很有用.但是,在堆上分配的任何内容都会持续,只要我们需要它,并且可以动态调整大小,就像数组一样.这也很有用.我们可以用它来管理我们的内存分配.如果Foo类在其构造函数中在堆上分配了一些内存,并在其析构函数中删除了该内存,该怎么办?然后我们可以充分利用两个世界,安全的内存分配,保证再次释放,但没有强制一切都在堆栈上的限制.
这几乎是大多数C++代码的工作原理.以标准库std::vector
为例.这通常在堆栈上分配,但可以动态调整大小和调整大小.它通过在内部根据需要在堆上分配内存来实现.该类的用户永远不会看到这一点,因此没有机会泄漏内存,或者忘记清理你分配的内容.
这个原则称为RAII(资源获取是初始化),它可以扩展到必须获取和释放的任何资源.(网络套接字,文件,数据库连接,同步锁).所有这些都可以在构造函数中获取,并在析构函数中释放,因此您可以保证获得的所有资源都将被释放.
作为一般规则,永远不要直接从高级代码中使用new/delete.始终将它包装在一个可以为您管理内存的类中,这将确保它再次被释放.(是的,这个规则可能有例外.特别是,智能指针要求你new
直接调用,并将指针传递给它的构造函数,然后接管并确保delete
正确调用.但这仍然是一个非常重要的经验法则)
我应该使用哪种方法?
这几乎不是由您的输入首选项决定的,而是由上下文决定的.如果您需要将对象保留在几个堆栈中,或者如果它对于堆栈来说太重,则将其分配给免费存储.此外,由于您正在分配对象,因此您还负责释放内存.查找delete
运营商.
为了简化使用空闲存储管理人员的负担已经发明的东西像auto_ptr
和unique_ptr
.我强烈建议你看看这些.他们甚至可能对你的打字问题有帮助;-)
如果您使用C++编写,那么您可能正在为性能而写作.使用new和free存储比使用堆栈要慢得多(特别是在使用线程时),所以只在需要时使用它.
正如其他人所说,当对象需要在函数或对象范围之外生存时,对象非常大或者在编译时不知道数组的大小时,需要新的.
另外,尽量避免使用删除.将您的新内容包装成智能指针.让智能指针调用为您删除.
在某些情况下智能指针不智能.切勿将std :: auto_ptr <>存储在STL容器中.由于容器内的复制操作,它将很快删除指针.另一种情况是你有一个非常大的STL容器指向对象的指针.boost :: shared_ptr <>会有大量的速度开销,因为它会上下调整引用计数.在这种情况下更好的方法是将STL容器放入另一个对象,并为该对象提供一个析构函数,该析构函数将在容器中的每个指针上调用delete.
简短的回答是:如果你在C ++初学者,你应该永远不会使用new
或delete
自己。
相反,您应该使用智能指针,例如std::unique_ptr
和std::make_unique
(或更不经常使用std::shared_ptr
and std::make_shared
)。这样,您不必担心内存泄漏。而且,即使你是更先进的,最好的做法通常是封装你使用自定义的方式new
和delete
成小类(如自定义智能指针),专门只是对象生命周期的问题。
当然,在后台,这些智能指针仍在执行动态分配和释放,因此使用它们的代码仍将具有关联的运行时开销。这里的其他答案涵盖了这些问题,以及如何在何时使用智能指针而不是仅在堆栈上创建对象或将它们合并为对象的直接成员方面做出设计决策,我将不再赘述。但我的执行摘要是:在某些情况迫使您使用之前,不要使用智能指针或动态分配。