Click to See Complete Forum and Search --> : Smart Pointer Question.


Amish79
January 6th, 2005, 08:23 AM
When is it advantageous to use smart pointer? &
When it is disadvantageous to use smart pointers?

It would be good if you can provide me with an example which can explain me both the above suitations.

I appreciate your effort.

Thanks in advance,
Amish.

RoboTact
January 6th, 2005, 12:48 PM
Smart pointer can delete object, pointer to which it contains, you don't need to write delete code in destructor every time you add an object by pointer to your class definition (if that class owns that object) and you don't need to check if the object can be deleted and when it should be deleted if this object is not owned by any class. You can pass "copy" of class with some kind of smart pointer (I don't know it it still can be named "smart pointer"), creating another smart pointer as a copy, ang getting read only access to the same object and octually copying it when change is required ("delayed copy", like unix fork, CString in MFC). You can implement all this object managment control, that's it.

Hediea
January 6th, 2005, 01:58 PM
When is it advantageous to use smart pointer? &
When it is disadvantageous to use smart pointers?

It would be good if you can provide me with an example which can explain me both the above suitations.

I appreciate your effort.

Thanks in advance,
Amish.
I tried o search the net for yuo an example, didn't know how it should work cause i am also slow in programming.
class sp
{
public:
int del;
sp* operator ->() { return this;};
};

int main()
{
sp t;
cout << t->del;
}

wien
January 6th, 2005, 02:48 PM
I tried o search the net for yuo an example, didn't know how it should work cause i am also slow in programming. That's no smart-pointer. In fact, I'm not sure what that is supposed to be. Looks like an example of operator -> overloading.

Anyhoo... A smart pointer (like for instance std::auto_ptr or boost::shared_ptr) can for example be of great help when using dynamically allocated memory, while keeping the code exception safe. Consider the following example:try
{
int* ptr = new int;
throw 0;
}
catch(int ex)
{}Here the pointer to the memory allocated by new will be lost, and you have a memory leak. By using a smart pointer instead, the deallocation will be handeled automatically, and no memory leaks occur. Like so:try
{
std::auto_ptr<int> ptr = new int;
throw 0;
}
catch(int ex)
{}Reference counted smart pointers like boost::shared_ptr, can also be useful when multiple objects need to have a reference to the same object, and the destruction order of these objects is not obvious.

But even though they seem cool and the temptation to "go Java" (Use new all over the place, in combination with smart pointers) is great, you should be absolutely sure that a smart pointer, or more specifically dynamically allocated memory, is really what you need before you use them. Only use dynamically allocated memory if you have a really compelling reason to do so. Otherwise make do with the stack, and the standard containers.

Andreas Masur
January 6th, 2005, 03:29 PM
try
{
std::auto_ptr<int> ptr = new int;
throw 0;
}
catch(int ex)
{}
Well...it will rather be
try
{
std::auto_ptr<int> ptr(new int);
throw 0;
}
catch(int ex)
{}
The standard smart pointer does not allow initialization with an ordinary pointer using assignment.

wien
January 6th, 2005, 03:33 PM
Woops.. Right you are sir! :blush:

marten_range
January 6th, 2005, 04:06 PM
The purpose of almost all smart pointers are to help manage object lifetimes.

Common smart pointers are:

std::auto_ptr
CComPtr
CComQIPtr
_com_ptr_t
boost::shared_ptr
boost::intrusive_ptr
boost::scoped_ptr
Loki::SmartPtr

And the list goes on.

They are same but different so to say. They are all same in the way that they all try to help you manage object lifetimes but they are different in that they fit for different situations.

Loki::SmartPtr is the one-size-fits-all attempt by Andrei Alexandrescu.

Although implicitly given by what wien said I thought I just point out that it's not only in the presence of exceptions that you can get problem with memory leaks when not using smart pointers.

Consider:

void f( int const action )
{
int * l_ptr(new int(3));

if( action == 0 )
{
return; // memory leak
}
else if( action == 1 )
{
throw std::runtime_error("error"); // memory leak
}
else if( action == 2 )
{
l_ptr = new int(4); // memory leak
}

delete l_ptr;
}


Of course, when you write the code you know how it's supposed to be used but then the maintenance programmer comes in and he/she has to apply a quick fix. He/she might then not be aware on how the life-time in your application is handled and might introduce memory leaks.

One might think that using single point of return plus try...catch is enough to deal with memory management without smart pointer but exceptions can occur in the darnest of players (consider http://www.gotw.ca/gotw/020.htm) and it can be quite easy to miss a place.

By using a smart pointer it guarantees that the pointee destructor will be called when the smart pointer goes out of scope.

So smart pointers helps with object lifetimes. That's all well and good.

The problem of many smart pointers implementations is that they desperatly try to look as a ordinary plain pointers which can be confusing because a smart pointer is _not_ a plain pointer. Common mistake by smart pointer implementations is that they overload operator & to mimic the plain pointer (CComPtr and CComQIPtr has been notorius for this). But this also makes these smart pointers incompatible with standard containers (which requires a operator & to behave like it defined in the standard). Also many smart pointers (again CComPtr and CComQIPtr) allows implicit conversion to the pointee type which has created many memory leaks over the years. It's all well intended but the road to heck is paved with good intentions.

Also when using smart pointers the pointee class destructor must _not_ throw exceptions. This is very important!

Consider this:

struct x
{
~x()
{
throw std::runtime_error("error");
}
};

void f()
{
std::auto_ptr<x> const l_ptr(new x());
throw std::runime_error("error"); // here exit(0) will be called
}


So at the second throw the whole program terminates. This is according to the standard (to be honest I don't have the standard before so I don't know if this is a hard fact). This is because when reaching the second throw an exception is placed on the "stack" (not sure of the exact wording here). std::auto_ptr goes out of scope and ~x() is called. Then a new exception is thrown but there is already one "active". This is illegal and the c++ runtime shall terminate the app.

Can be suprising and hard to find.


So drawback of smart pointers are:
1. They are not plain pointers even if many smart pointers tries very hard to look like one.
2. The destructor of the pointee type must not throw.
3. Some smart pointers are incompatible with the standard containers (but then the plain pointers never really were compatible either)


There are more but I'm just not smart enough right now to think of more and the post is long enough already.

The big question is then:

Is it worth it to use smart pointers.

And my answer is:
Yes!

Always, always use smart pointers. Get used to using smart pointers and scope guards. Feel comfortable with smart pointers and use smart pointers of good quality such as boost::shared_ptr (and the rest of the boost smart pointers) or Loki::SmartPtr.

Hope this helps

PS. Lot of warning have been made around std::auto_ptr and it's correct that you should understand what std::auto_ptr is intended for but std::auto_ptr is one of the best smart pointers ever made. Sometimes it's _exactly_ what you want. The problem with std::auto_ptr is that it's shipped with STL but it can't be used in the standard containers => confusing.

wien
January 6th, 2005, 04:13 PM
Also when using smart pointers the pointee class destructor must _not_ throw exceptions. This is very important!This is in fact something that is good to keep as a rule of thumb all the time. You can get yourself into the same kind of mess even if you don't use smart pointers:struct x
{
~x()
{
throw std::runtime_error("error");
}
};

void f()
{
x an_x;
throw std::runime_error("error"); // just as bad...
}

Graham
January 6th, 2005, 05:30 PM
In fact, go one further: no destructor should ever be allowed to throw an exception. Destructors that throw are evil and are the cause of buggy programs, system crashes and chicken pox. ( :rolleyes: )

KevinHall
January 6th, 2005, 11:57 PM
Doesn't the C++ standard state that exceptions can't be thrown from destructors?

Andreas Masur
January 7th, 2005, 02:21 AM
Doesn't the C++ standard state that exceptions can't be thrown from destructors?
No...not really...it says in 15.3.2:

[Note: If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

IIRC correctly the standard does not say explicitly that destructors aren't allowed to throw in the common sense...

KevinHall
January 7th, 2005, 07:22 AM
I'll interpret that as:

One should not allow exceptions to escape a destructor or else terminate will be called.

That's a pretty strong discouragement there!

Andreas Masur
January 7th, 2005, 08:34 AM
I'll interpret that as:

[QUOTE]One should not allow exceptions to escape a destructor or else terminate will be called.[QUOTE]

That's a pretty strong discouragement there!
Well...I do not disagree....destructors actually should not throw anything...however, it is not completely prohibited by the standard (or at least I did not find it yet)...

KevinHall
January 7th, 2005, 10:20 AM
Even though the standard doesn't explicitly say one should not allow exceptions to be thrown from destructors, that's how people should interpret the standard. Calling terminate does indicate that your program won't continue working as otherwise expected!

Let's face the facts, when writing a destructor, people don't usually wrap the entire thing in a try/catch statement. I know I only do it when I think an exception is possible -- and that's on a good day when I remember to think about such things. So I believe the standardization comittee did the right thing here. They provided a controlled way to handle something people should not allow to happen (but that people will too often inadvertently allow to happen), rather than saying the behavior is undefined.

Apollyon
January 7th, 2005, 12:04 PM
What an instrusive_ptr? What do all the other smart_ptrs do? And who would name their namespaces "boost" and "Loki"?

KevinHall
January 7th, 2005, 12:12 PM
The superhackers will rule over the hackers. Mwhahaha; I'll rule you all!!!

who would name their namespaces "boost" and "Loki"?

:eek: :lol: You plan to rule over programmers and hackers and you don't know what boost is?

Seriously though, check out www.boost.org (http://www.boost.org) and The Loki Project page on SourceForge (http://sourceforge.net/projects/loki-lib/).

Apollyon
January 7th, 2005, 01:12 PM
That still doesn't answer my question though. What is an instrusive pointer and what do the other smart pointers do, besides auto_ptr?

wien
January 7th, 2005, 01:31 PM
That still doesn't answer my question though. What is an instrusive pointer and what do the other smart pointers do, besides auto_ptr?Read the boost smart_ptr docs. It's all in there!

Apollyon
January 7th, 2005, 01:39 PM
But I don't have them!!! Stop giving me the run-around and just answer my question!!! Be nice!!!

wien
January 7th, 2005, 01:43 PM
If you bothered to look at the link Kevin gave you, you would find it:

http://www.boost.org/libs/smart_ptr/smart_ptr.htm

_uj
January 8th, 2005, 08:31 PM
But even though they seem cool and the temptation to "go Java" (Use new all over the place, in combination with smart pointers) is great, you should be absolutely sure that a smart pointer, or more specifically dynamically allocated memory, is really what you need before you use them. Only use dynamically allocated memory if you have a really compelling reason to do so. Otherwise make do with the stack, and the standard containers.

I think that to "go Java" all the way would mean that a general garbage collector was included in the C++ standard. But it isn't and probably never will be. I've been looking around for one but there doesn't seem to be much activity in this area. With a good GC many needs for smart pointers would go away.

If you want to use object oriented programming in C++ you must allocate objects on the heap to avoid the so called slicing problem. And if you allocate objects on the heap you also have the responsibility of de-allocating them again when you're finished with them. This is really tricky and error-prone and smart pointers are of great help at this. So one "compelling" reason to use smart pointers is when you want to use OO techniques but don't want to pay with memory leaks :p .

The one situation smart pointers never can handle is so called circular references. This is when objects eventually point back at themeselves. An example would be a circular linked list.

Many have mentioned the Loki library. It's described in a book called Modern C++ Design. This really is the place to look for a good description of smart pointers (and other advanced C++ techniques).

wien
January 9th, 2005, 07:21 PM
I think that to "go Java" all the way would mean that a general garbage collector was included in the C++ standard.Yes it would, but I didn't mean that literally. Just a "funny" way of saying don't use the heap for all your allocation needs. :)With a good GC many needs for smart pointers would go away.To be honest with you, I don't really see the point of a garbage collector in C++. We have a (in my opinion) better mechanism called the stack, which when combined with destructors, enable us to eliminate most (if not all) of the needs for a GC. If you follow the coding guidelines preached by many codeguru posters (use the stack and standard containers), you will have to be good to end up with a memory leak. ;) If you manage to find a problem you can't solve using those techniques, then smart pointers usually fill that hole. I have never found myself thinking, "Gosh, wish I had a garbage collector." because... well... I never needed one.If you want to use object oriented programming in C++ you must allocate objects on the heap to avoid the so called slicing problem.Umm... Not quite sure what you mean here. The slicing problem occurs regardless if you allocate on the heap or the stack. And if you allocate objects on the heap you also have the responsibility of de-allocating them again when you're finished with them. This is really tricky and error-prone and smart pointers are of great help at this.Which is why you should not allocate on the heap unless you really need to. Heap allocation is no requirement for using OO techniques. (Of course this depends on what exactly you are talking about when you say "OO techniques".)The one situation smart pointers never can handle is so called circular references. This is when objects eventually point back at themeselves. An example would be a circular linked list.Very true, but smart-pointers in C++ are not intended to be used for everything. They have their uses, but you need to be very aware of what you are doing, so you don't get yourself into problems like that. That's why I warned Hediea from "going Java" in the first place! :)

marten_range
January 10th, 2005, 03:18 AM
Umm... Not quite sure what you mean here. The slicing problem occurs regardless if you allocate on the heap or the stack

Objects allocated on the heap (is there any good standard C++ conformant word for such objects?) may have (and should have) their copy-constructor and assignement operator declared private. Thus making slicing a tad more difficult to happen by mistake.

_uj
January 10th, 2005, 03:32 AM
To be honest with you, I don't really see the point of a garbage collector in C++.
Object orientation basically imposes dynamic object allocations so there's no way around a heavy heap usage if you want OO in an application.

Unfortunately the standard heap mechanism is usually very slow. To overcome this efficiency problem you need to use a "smarter" object allocator (such as the one available in Loki). Now the conceptual leap from a "smart" object allocator to a full-blown GC isn't that big.

I think it would be great if there was a GC in the C++ standard. For my own convinience of course but it would also help preventing a quite nasty development, namely a forking of C++. I think this has happened in Visual C++ .NET where you're encouraged to use non-standard C++ syntax to make use of a built-in GC. Really great, unless you want portable code!


Umm... Not quite sure what you mean here. The slicing problem occurs regardless if you allocate on the heap or the stack.

Then there's no way of getting rid of it then :p. No just kidding. You get rid of the slicing problem by using reference semantics for objects only. This means you always use references (pointers) when assigning objects and never copy their values.

----------

So okay, if you use the class mechanism only to create small concrete classes then value sematics is fine (and the stack & STL containers are sufficient). But as soon as you want to use OO features such as pure virtual classes and class derivation then there's no way around the heap.

PS. If anyone knows about the existence of a good standard C++ garbage collector please advice.

Graham
January 10th, 2005, 04:56 AM
Objects allocated on the heap (is there any good standard C++ conformant word for such objects?) may have (and should have) their copy-constructor and assignement operator declared private. Thus making slicing a tad more difficult to happen by mistake. the descision about whether to disable copying should be based on the semantics of the class (i.e. does copying make sense?) rather than on usage or otherwise of the free store. After all, unless you code it specifically, there's no reason that a given class can't be instantiated on the stack or the free store as needs dictate.