114

I'm a C programmer trying to understand C++. Many tutorials demonstrate object instantiation using a snippet such as:

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?

Thanks!

el_champo
  • 1,219
  • 3
  • 10
  • 8

9 Answers9

169

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.

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.

The most common name for this idiom is 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.

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.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
jalf
  • 243,077
  • 51
  • 345
  • 550
  • Raw pointers are useful when you want auto_ptr-like semantics (transferring ownership), but retain the non-throwing swap operation, and don't want the overhead of reference counting. Edge case maybe, but useful. – Greg Rogers Dec 02 '08 at 14:25
  • 2
    This is a correct answer, but the reason I would never get into the habit of creating objects on the stack is because its never completely obvious how big that object will be. You're just asking for a stack-overflow exception. – dviljoen Dec 02 '08 at 14:30
  • 3
    Greg: Certainly. But as you say, an edge case. In general, pointers are best avoided. But they're in the language for a reason, no denying that. :) dviljoen: If the object is big, you wrap it in a RAII object, which can be allocated on the stack, and contains a pointer to heap-allocated data. – jalf Dec 02 '08 at 16:52
  • 3
    @dviljoen: No I'm not. C++ compilers don't unnecessarily bloat objects. The worst you'll see is typically that it gets rounded up to the nearest multiple of four bytes. Usually, a class containing a pointer will take as much space as the pointer itself, so it costs you nothing in stack usage. – jalf Apr 18 '09 at 13:30
20

Though having things on the stack might be an advantage in terms of allocation and automatic freeing, it has some disadvantages.

  1. You might not want to allocate huge objects on the Stack.

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

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.

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.

UniversE
  • 2,419
  • 17
  • 24
  • 2
    -1: people not able to understand value semantics & the simple fact that polymorphism needs a reference/pointer (to avoid object slicing) do not constitute any kind of issue with the language. c++'s power should not be considered a drawback just because some folk can't learn its basic rules. – underscore_d Feb 23 '16 at 19:56
  • Thanks for the heads up. I had similar issue with method taking in object instead of pointer or reference, and what a mess it was. The object has pointers inside and they got deleted too soon because of this coping. – BoBoDev Nov 03 '17 at 22:53
  • @underscore_d I agree; the last paragraph of this answer should be removed. Don't take the fact that I edited this answer to mean I agree with it; I just didn't want the mistakes in it to be read. – TamaMcGlinn Sep 01 '18 at 19:43
  • @TamaMcGlinn agreed. Thanks for the good edit. I removed the opinion part. – UniversE Sep 01 '18 at 20:24
13

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!

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.

PierreBdR
  • 42,120
  • 10
  • 46
  • 62
13

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.

void FeedTheDog(Dog* hungryDog);

Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;

Dog goodDog;
FeedTheDog(&goodDog);
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Alternatively: void FeedTheDog(Dog& hungryDog); Dog dog; FeedTheDog(dog); – Scott Langham Nov 12 '15 at 16:54
  • 1
    @ScottLangham true, but that wasn't the point I was trying to make. Sometimes you're calling a function that takes an optional NULL pointer for example, or maybe it's an existing API that can't be changed. – Mark Ransom Nov 12 '15 at 17:25
7

Treat heap as a very important real estate and use it very judiciously. The basic thumb rule is to use stack whenever possible and 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

(2). You need not worry about memory fragmentation caused by the allocating more space than necessary by your heap manager.

dangerousdave
  • 6,331
  • 8
  • 45
  • 62
Naveen
  • 74,600
  • 47
  • 176
  • 233
  • 1
    There should be some consideration as to the size of the object. The Stack is size limited, so very large objects should be Heap allocated. – Adam Dec 07 '12 at 01:56
  • 1
    @Adam I think that Naveen is still correct here. If you can put a large object on the stack, then do it, because it's better. There are a lot of reasons you probably can't. But I think he's correct. – McKay Sep 25 '13 at 17:12
5

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,

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.

Roddy
  • 66,617
  • 42
  • 165
  • 277
1

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.

Scott Langham
  • 58,735
  • 39
  • 131
  • 204
0

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.

Community
  • 1
  • 1
dangerousdave
  • 6,331
  • 8
  • 45
  • 62
  • 2
    You can work with stack based objects polymorphically too, I don't see any difference between stack/and heap allocated objects in this regard. Eg: void MyFunc() { Dog dog; Feed(dog); } void Feed(Animal& animal) { auto food = animal->GetFavouriteFood(); PutFoodInBowl(food); } // In this example GetFavouriteFood can be a virtual function that each animal overrides with it's own implementation. This will work polymorphically with no heap involved. – Scott Langham Nov 12 '15 at 16:46
  • -1: polymorphism requires only a reference or pointer; it is totally agnostic of the storage duration of the underlying instance. – underscore_d Feb 23 '16 at 19:59
  • @underscore_d "Using a universal base class implies cost: Objects must be heap-allocated to be polymorphic;" - bjarne stroustrup http://www.stroustrup.com/bs_faq2.html#object – dangerousdave Feb 24 '16 at 11:16
  • LOL, I don't care if Stroustrup himself said it, but that's an incredibly embarrassing quote for him, because he's wrong. It's not hard to test this yourself, you know: instantiate some DerivedA, DerivedB, and DerivedC on the stack; instantiate also a stack-allocated pointer to Base and confirm that you can seat it with A/B/C and use them polymorphically. Apply critical thought and Standard reference to claims, even if they are by the original author of the language. Here: http://stackoverflow.com/q/5894775/2757035 – underscore_d Feb 24 '16 at 11:23
  • Put it this way, I have an object containing 2 separate families of nearly 1000 each of polymorphic objects, with automatic storage duration. I instantiate this object on the stack, and by referring to those members via references or pointers, it attains the full capabilities of polymorphism over them. Nothing in my program is dynamically/heap-allocated (aside from resources owned by stack-allocated classes), and it does not change the capabilities of my object by an iota. – underscore_d Feb 24 '16 at 11:30
-4

I had the same problem in Visual Studio. You have to use:

yourClass->classMethod();

rather than:

yourClass.classMethod();

Hamed
  • 11
  • 3
    This does not answer the question. You rather note one has to use different syntax to access object allocated on heap (via the pointer to the object) and object allocated on stack. – Alexey Ivanov Aug 15 '12 at 10:19