C++ 对象实例化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/333443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ Object Instantiation
提问by el_champo
I'm a C programmer trying to understand C++. Many tutorials demonstrate object instantiation using a snippet such as:
我是一个试图理解 C++ 的 C 程序员。许多教程使用一个片段来演示对象实例化,例如:
Dog* sparky = new Dog();
which implies that later on you'll do:
这意味着稍后您将执行以下操作:
delete sparky;
which makes sense. Now, in the case when dynamic memory allocation is unnecessary, is there any reason to use the above instead of
这是有道理的。现在,在不需要动态内存分配的情况下,是否有任何理由使用上述而不是
Dog sparky;
and let the destructor be called once sparky goes out of scope?
一旦sparky超出范围,就让析构函数被调用?
Thanks!
谢谢!
回答by jalf
On the contrary, you should always prefer stack allocations, to the extent that as a rule of thumb, you should never have new/delete in your user code.
相反,您应该始终更喜欢堆栈分配,因为根据经验,您永远不应该在用户代码中使用 new/delete。
As you say, when the variable is declared on the stack, its destructor is automatically called when it goes out of scope, which is your main tool for tracking resource lifetime and avoiding leaks.
如您所说,当变量在堆栈上声明时,它的析构函数在超出范围时会自动调用,这是您跟踪资源生命周期和避免泄漏的主要工具。
So in general, every time you need to allocate a resource, whether it's memory (by calling new), file handles, sockets or anything else, wrap it in a class where the constructor acquires the resource, and the destructor releases it. Then you can create an object of that type on the stack, and you're guaranteed that your resource gets freed when it goes out of scope. That way you don't have to track your new/delete pairs everywhere to ensure you avoid memory leaks.
所以一般来说,每次需要分配资源时,无论是内存(通过调用 new)、文件句柄、套接字还是其他任何东西,都将它包装在一个类中,在该类中构造函数获取资源,析构函数释放它。然后,您可以在堆栈上创建该类型的对象,并保证您的资源在超出范围时被释放。这样您就不必到处跟踪新/删除对以确保避免内存泄漏。
The most common name for this idiom is RAII
这个习语最常见的名字是RAII
Also look into smart pointer classes which are used to wrap the resulting pointers on the rare cases when you do have to allocate something with new outside a dedicated RAII object. You instead pass the pointer to a smart pointer, which then tracks its lifetime, for example by reference counting, and calls the destructor when the last reference goes out of scope. The standard library has std::unique_ptr
for simple scope-based management, and std::shared_ptr
which does reference counting to implement shared ownership.
还要查看智能指针类,这些类用于在极少数情况下包装结果指针,当您必须在专用 RAII 对象之外使用 new 分配某些内容时。您改为将指针传递给智能指针,然后智能指针跟踪其生命周期,例如通过引用计数,并在最后一个引用超出范围时调用析构函数。标准库std::unique_ptr
用于简单的基于范围的管理,并std::shared_ptr
通过引用计数来实现共享所有权。
Many tutorials demonstrate object instantiation using a snippet such as ...
许多教程使用一个片段来演示对象实例化,例如...
So what you've discovered is that most tutorials suck. ;) Most tutorials teach you lousy C++ practices, including calling new/delete to create variables when it's not necessary, and giving you a hard time tracking lifetime of your allocations.
所以你发现大多数教程都很烂。;) 大多数教程教你糟糕的 C++ 实践,包括在不需要时调用 new/delete 来创建变量,并让你很难跟踪分配的生命周期。
回答by UniversE
Though having things on the stack might be an advantage in terms of allocation and automatic freeing, it has some disadvantages.
尽管将东西放在堆栈上可能在分配和自动释放方面具有优势,但它也有一些缺点。
You might not want to allocate huge objects on the Stack.
Dynamic dispatch! Consider this code:
您可能不想在堆栈上分配巨大的对象。
动态调度!考虑这个代码:
#include <iostream>
class A {
public:
virtual void f();
virtual ~A() {}
};
class B : public A {
public:
virtual void f();
};
void A::f() {cout << "A";}
void B::f() {cout << "B";}
int main(void) {
A *a = new B();
a->f();
delete a;
return 0;
}
This will print "B". Now lets see what happens when using Stack:
这将打印“B”。现在让我们看看使用 Stack 时会发生什么:
int main(void) {
A a = B();
a.f();
return 0;
}
This will print "A", which might not be intuitive to those who are familiar with Java or other object oriented languages. The reason is that you don't have a pointer to an instance of B
any longer. Instead, an instance of B
is created and copied to a
variable of type A
.
这将打印“A”,对于熟悉 Java 或其他面向对象语言的人来说,这可能不太直观。原因是您不再有指向实例的指针B
。相反,B
会创建一个 的实例并将其复制到a
类型为 的变量中A
。
Some things might happen unintuitively, especially when you are new to C++. In C you have your pointers and that's it. You know how to use them and they do ALWAYS the same. In C++ this is not the case. Just imagine what happens, when you use a in this example as an argument for a method - things get more complicated and it DOES make a huge difference if a
is of type A
or A*
or even A&
(call-by-reference). Many combinations are possible and they all behave differently.
有些事情可能会以非直觉方式发生,尤其是当您不熟悉 C++ 时。在 C 中,你有你的指针,就是这样。您知道如何使用它们,而且它们的作用始终相同。在 C++ 中,情况并非如此。试想会发生什么,当你在这个例子中使用作为一个方法的参数-事情变得更复杂,但它使一个巨大的区别,如果a
是类型A
或者A*
甚至A&
(呼叫通过引用)。许多组合都是可能的,它们的行为都不同。
回答by PierreBdR
Well, the reason to use the pointer would be exactly the same that the reason to use pointers in C allocated with malloc: if you want your object to live longer than your variable!
嗯,使用指针的原因与在 C 中使用指针分配的 malloc 的原因完全相同:如果你希望你的对象比你的变量活得更久!
It is even highly recommended to NOT use the new operator if you can avoid it. Especially if you use exceptions. In general it is much safer to let the compiler free your objects.
如果可以避免,甚至强烈建议不要使用 new 运算符。特别是如果您使用异常。一般来说,让编译器释放你的对象要安全得多。
回答by Mark Ransom
I've seen this anti-pattern from people who don't quite get the & address-of operator. If they need to call a function with a pointer, they'll always allocate on the heap so they get a pointer.
我已经从那些不太了解 & address-of 运算符的人那里看到了这种反模式。如果他们需要使用指针调用函数,他们将始终在堆上分配,以便获得一个指针。
void FeedTheDog(Dog* hungryDog);
Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;
Dog goodDog;
FeedTheDog(&goodDog);
回答by Naveen
Treat heap as a very important real estate and use it very judiciously. The basic thumb rule is to use stack whenever possibleand use heap whenever there is no other way. By allocating the objects on stack you can get many benefits such as:
将堆视为非常重要的不动产,并非常明智地使用它。基本的经验法则是使用堆栈尽可能和使用堆每当有没有其他办法。通过在堆栈上分配对象,您可以获得许多好处,例如:
(1). You need not have to worry about stack unwinding in case of exceptions
(1). 您不必担心发生异常时的堆栈展开
(2). You need not worry about memory fragmentation caused by the allocating more space than necessary by your heap manager.
(2). 您无需担心由于堆管理器分配的空间超出所需空间而导致的内存碎片。
回答by Roddy
The only reason I'd worry about is that Dog is now allocated on the stack, rather than the heap. So if Dog is megabytes in size, you may have a problem,
我担心的唯一原因是 Dog 现在分配在堆栈上,而不是堆上。所以如果 Dog 的大小是兆字节,你可能会遇到问题,
If you do need to go the new/delete route, be wary of exceptions. And because of this you should use auto_ptr or one of the boost smart pointer types to manage the object lifetime.
如果您确实需要走新的/删除路线,请注意例外情况。因此,您应该使用 auto_ptr 或 boost 智能指针类型之一来管理对象生命周期。
回答by Scott Langham
There's no reason to new (on the heap) when you can allocate on the stack (unless for some reason you've got a small stack and want to use the heap.
当您可以在堆栈上分配时(除非由于某种原因您有一个小堆栈并想要使用堆),因此没有理由在堆上新建(在堆上)。
You might want to consider using a shared_ptr (or one of its variants) from the standard library if you do want to allocate on the heap. That'll handle doing the delete for you once all references to the shared_ptr have gone out of existance.
如果您确实想在堆上分配,您可能需要考虑使用标准库中的 shared_ptr(或其变体之一)。一旦对 shared_ptr 的所有引用都不存在,这将为您处理删除操作。
回答by dangerousdave
There is an additional reason, which no one else has mentioned, why you might choose to create your object dynamically. Dynamic, heap based objects allow you to make use of polymorphism.
还有一个其他人没有提到的原因,为什么您可能会选择动态创建对象。动态的、基于堆的对象允许您利用多态性。
回答by Hamed
I had the same problem in Visual Studio. You have to use:
我在 Visual Studio 中遇到了同样的问题。你必须使用:
yourClass->classMethod();
yourClass->classMethod();
rather than:
而不是:
yourClass.classMethod();
yourClass.classMethod();