views:

1088

answers:

7

I understand that this may be construed as one of those "what's your preference" questions, but I really want to know why you would choose one of the following methods over the other.

Suppose you had a super complex class, such as:


class CDoSomthing {

    public:
        CDoSomthing::CDoSomthing(char *sUserName, char *sPassword)
        {
            //Do somthing...
        }

        CDoSomthing::~CDoSomthing()
        {
            //Do somthing...
        }
};

How should I declare a local instance within a global function?


int main(void)
{
    CDoSomthing *pDoSomthing = new CDoSomthing("UserName", "Password");

    //Do somthing...

    delete pDoSomthing;
}

-- or --


int main(void)
{
    CDoSomthing DoSomthing("UserName", "Password");

    //Do somthing...

    return 0;
}
+26  A: 

Prefer local variables, unless you need the object's lifetime to extend beyond the current block. (Local variables are the second option). It's just easier than worrying about memory management.

P.S. If you need a pointer, because you need it to pass to another function, just use the address-of operator:

SomeFunction(&DoSomthing);
Mark Ransom
What does the second option does?
OscarRyz
@Reyes, Yes. Your second example depends on RAII. In this case it doesn't matter (unless you call main ...), but for many other cases, exception handling is an issue with pointers, and depending on RAII is safer.
strager
I feel the same way about not worrying about the memory managment, but i'm one of those "old school" developers that just has to see a matching free() for every malloc(). I guess I extend that to my classes, but it cost alot of extra typing.
NTDLS
Mismatching new/delete (or malloc/free) would be relevant if we were talking about smart pointers, but we're not. Both examples are matched, the first with one new and one delete, and the second with none.
Mark Ransom
Or unless you're working in an environment with a small stack (I'm looking at you, Windows CE...)
Tim Lesher
NTDLS: That's not old school, it's just *extremely* bad practice in C++. It means that a whole class of nasty errors become possible that simply *could not occur* if you'd used RAII. In C, you obviously don't have a choice. And that is why C++ should not be treated as C with classes.
jalf
+3  A: 

The second version will unwind the stack if an exception is thrown. The first will not. I don't see much difference otherwise.

Paul Beckingham
Very, very good point! Thank you!
NTDLS
One's allocated to the heap (unless operator new is overriden), and the other is allocated on the stack.
strager
@strager - the first has a pointer on the stack and an object on the heap, the latter is all stack.
Paul Beckingham
A: 

Mark Ransom is right, also you'll need to instantiate with new if you are going to pass the variable as a parameter to the CreateThread-esque function.

arul
+13  A: 

The second form is the so called RAII (Resource Acquisition Is Initialization) pattern. It has many advantages over the first.

When use new, you have to use delete yourself, and guarantee it will always be deleted, even if an exception is thrown. You must guarantee all that yourself.

If you use the second form, when the variable goes out of scope, it is always cleaned up automatically. And if an exception is thrown, the stack unwinds and it is also cleaned up.

So, you should prefer RAII (the second option).

Martinho Fernandes
Doesn't the second need a variable to work with?
OscarRyz
@Oscar: Yes, and that variable is called DoSomthing in the example on the question.
Martinho Fernandes
Got it then it would be: DoSomthing.Method(); right?
OscarRyz
I would appreciate if I could be enlightened about the downvote. I'm always glad to learn where I failed.
Martinho Fernandes
I didn't do the downvote, but... My understanding of RAII is that an object owns and manages a resource. In this example, the object IS the resource, so I'm not sure the acronym applies.
Mark Ransom
@Mark: Ok, I understand why someone would see things that way. Maybe there's a different name, but I don't think that distinction is really important.
Martinho Fernandes
+19  A: 

There are two main considerations when you declare a variable on the stack vs. in the heap - lifetime control and resource management.

Allocating on the stack works really well when you have tight control over the lifetime of the object. That means you are not going to pass a pointer or a reference of that object to code outside of the scope of the local function. This means, no out parameters, no COM calls, no new threads. Quite a lot of limitations, but you get the object cleaned up properly for you on normal or exceptional exit from the current scope (Though, you might want to read up on stack unwinding rules with virtual destructors). The biggest drawback of the stack allocation - the stack is usually limited to 4K or 8K, so you might want to be careful what you put on it.

Allocating on the heap on the other hand would require you to cleanup the instance manually. That also means that you have a lot of freedom how to control the lifetime of the instance. You need to do this in two scenarios: a) you are going to pass that object out of scope; or b) the object is too big and allocating it on the stack could cause stack overflow.

BTW, a nice compromise between these two is allocating the object on the heap and allocating a smart pointer to it on the stack. This ensures that you are not wasting precious stack memory, while still getting the automatic cleanup on scope exit.

Franci Penov
very nice explanation
MahlerFive
I think "the stack is usually limited to 4K or 8K" doesn't apply so much these days. I just did an experiment with Visual C++ 2005 express and found I could put a char array of up to 0xfbf8c (=1032076) bytes on the stack of a hello world app okay. One more and boom - stack overflow. Otherwise nice.
Bill Forster
For Windows, the stack is limited to 1 MB by default (controllable at thread creation time) in user mode, 12 kB in x86 kernel mode, and 24 kB in x64 kernel mode. Linux on x86 has an 8 kB or 4 kB stack in kernel mode depending on configuration options; maybe that's what you were thinking of?
bk1e
Lol, I was thinking about DOS. I haved not looked at the stack size in a while. You know, C# is not really inducive to looking at the memory layout... :-)
Franci Penov
+6  A: 

In addition to what has been said so far, but there are additional performance considerations to be taken into account, particularly in memory-allocation-intensive applications:

  1. Using new will allocate memory from the heap. In the case of intense (extremely frequent) allocation and deallocation, you will be paying a high price in:
    • locking: the heap is a resource shared by all threads in your process. Operations on the heap may require locking in the heap manager (done for you in the runtime library), which may slow things down significantly.
    • fragmentation: heap fragments. You may see the time it takes malloc/new and free/delete to return increase 10-fold. This compounds with the locking problem above, as it takes more time to manage a fragmented heap and more threads queue up waiting for the heal lock. (On Windows there is a special flag you can set for the heap manager so it heuristically attempts to reduce fragmentation.)
  2. Using the RAII pattern, memory is simply taken off the stack. Stack is a per-thread resource, it does not fragment, there is no locking involved, and may turn out to play in your advantage in terms of memory locality (i.e. memory caching at the CPU level.)

So, when you need objects for a brief (or scoped) period of time, definitely use the second approach (local variable, on the stack.) If you need to share data between threads, use new/malloc (on one hand you have to, on the second hand these objects are typically long-lived enough so you pay essentially 0-cost vis-a-vis the heap manager.)

Cheers, V.

vladr
Just to clarify, you mistyped it. It's RAII. And it wasn't my idea to call it that. It's a well-known pattern, aparently invented by Bjarn Stroustroup.
Martinho Fernandes
+2  A: 

The biggest difference between the two is that the new initiates a pointer to the object.

By creating the object without new, the object initiated is stored on the stack. If it is initiated with new, it returns a pointer to the new object that has been created on the heap. It actually returns a memory address that points to the new object. When this happens, you need to memory manage the variable. When you are done using the variable, you would need to call delete on it to avoid a memory leak. Without the new operator, when the variable goes out of scope the memory will be freed automatically.

So if you need to pass the variable outside of the current scope, using new is more efficient. However, if you need to make a temporary variable, or something that will only be used temporarily, having the object on the stack is going to be better, since you don't have to worry about memory management.

Seburdis