我应该什么时候在C ++中使用新的关键字?
我一直在使用C ++,而且我一直在想新的关键字。 简单地说,我应该使用它,还是不?
1)使用新的关键字...
MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";
2)没有新的关键字...
MyClass myClass;
myClass.MyField = "Hello world!";
从实现的角度来看,他们似乎没有什么不同(但我确定他们是)......但是,我的主要语言是C#,当然第一种方法是我习惯的。
难度似乎是方法1更难用于std C ++类。
我应该使用哪种方法?
更新1:
我最近使用堆栈内存(或免费存储)的新关键字作为超出范围的大型数组(即从函数返回)。 在我使用堆栈之前,导致一半元素在范围之外被破坏,切换到堆使用确保了元素的完整性。 好极了!
更新2:
我的一位朋友最近告诉我,使用new
关键字有一个简单的规则; 每次键入new
,键入delete
。
Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.
这有助于防止内存泄漏,因为您始终必须将删除放在某处(即,将其剪切并粘贴到析构函数或其他位置时)。
方法1(使用new
)
delete
您的对象。 (如果你不删除它,你可能会造成内存泄漏) delete
它。 (即你可以return
一个你用new
创建的对象) delete
d; 无论采取哪种控制路径,或者抛出异常,都应该始终将其删除 。 方法2(不使用new
)
delete
它。 return
一个指向堆栈上的对象的指针) 至于使用哪一个; 鉴于上述限制,您选择最适合您的方法。
一些简单的情况:
delete
,(以及可能导致内存泄漏),你不应该使用new
。 new
两者之间有一个重要的区别。
一切不分配new
的表现很像C#值类型(人们通常会说,这些对象被分配在栈,这可能是最常见的/明显的情况下,但并非总是如此。更准确地说,不使用分配的对象new
有自动存储持续时间在堆中分配了new
分配的所有内容,并返回一个指向它的指针,与C#中的引用类型完全一样。
在堆栈上分配的任何东西都必须具有一个常量大小,在编译时确定(编译器必须正确设置堆栈指针,或者如果该对象是另一个类的成员,则必须调整该另一个类的大小) 。 这就是为什么C#中的数组是引用类型。 它们必须是,因为对于引用类型,我们可以在运行时决定要求多少内存。 这里同样适用。 只有具有常量大小(可在编译时确定的大小)的数组可以分配自动存储持续时间(在堆栈上)。 动态大小的数组必须通过调用new
来分配在堆上。
(这就是C#停止的地方)
现在,在堆栈中分配的任何内容都具有“自动”存储持续时间(实际上,您可以将变量声明为auto
,但如果未指定其他存储类型,则这是默认值,因此该关键字在实际中并未真正使用,但这是它来自)
自动存储时间意味着它听起来像什么,变量的持续时间自动处理。 相比之下,堆中分配的任何东西都必须由您手动删除。 这是一个例子:
void foo() {
bar b;
bar* b2 = new bar();
}
这个函数创建了三个值得考虑的值:
在第1行上,它在堆栈中声明了一个类型bar
的变量b
(自动持续时间)。
在第2行上,它在栈上声明了一个bar
指针b2
(自动持续时间),并调用new,在堆上分配一个bar
对象。 (动态持续时间)
当函数返回时,会发生以下情况:首先, b2
超出范围(销毁顺序始终与构造顺序相反)。 但是b2
只是一个指针,所以没有任何反应,它占用的内存就被释放了。 而且重要的是,它指向的内存(堆上的bar
实例)不会被触摸。 只有指针被释放,因为只有指针具有自动持续时间。 其次, b
超出范围,因为它具有自动持续时间,所以它的析构函数被调用,并且内存被释放。
和堆上的bar
实例? 它可能还在那里。 没人打扰删除它,所以我们泄漏了内存。
从这个例子中,我们可以看到任何具有自动持续时间的东西都会保证在析构函数超出作用域时调用它。 这很有用。 但是,只要我们需要,堆中分配的任何东西都会持续,并且可以像数组一样动态调整大小。 这也很有用。 我们可以使用它来管理我们的内存分配。 如果Foo类在其构造函数的堆中分配了一些内存,并在其析构函数中删除了该内存,该怎么办? 然后,我们可以获得两全其美的好处,即保证可以再次释放的安全内存分配,但没有强制所有内容的限制。
这几乎是大多数C ++代码的工作原理。 例如,看看标准库的std::vector
。 通常分配在堆栈上,但可以动态调整大小和调整大小。 它通过在内部根据需要在内部分配内存来实现这一点。 这个班级的用户从来没有看到这个,所以没有机会泄漏记忆,或忘记清理你分配的内容。
这个原则被称为RAII(资源获取是初始化),它可以扩展到任何必须获取和发布的资源。 (网络套接字,文件,数据库连接,同步锁)。 所有这些都可以在构造函数中获得,并在析构函数中发布,这样可以保证您获得的所有资源都将被释放。
作为一般规则,切勿直接从高级代码中使用new / delete。 总是将它包装在一个可以为你管理内存的类中,并且可以确保它可以再次释放。 (是的,这个规则可能有例外,特别是智能指针需要你直接调用new
,并将指针传递给它的构造函数,然后构造函数接管并确保正确调用delete
。但这仍然是一个非常重要的规则拇指)
我应该使用哪种方法?
这几乎从来不会由您的打字偏好而是由上下文决定。 如果您需要将对象保留在几个堆栈中,或者如果堆栈太重,请将其分配到免费商店中。 另外,由于您正在分配一个对象,因此您还负责释放内存。 查找delete
操作符。
为了减轻使用免费店铺管理的负担,人们发明了诸如auto_ptr
和unique_ptr
类的东西。 我强烈建议你看看这些。 他们甚至可能对你的打字问题有所帮助;-)
上一篇: When should I use the new keyword in C++?
下一篇: Why do Chrome and Firefox handle javascript variable set inside jQuery ajax() callback differently?