Poll: Do you use smart pointers?

J

Jens Theisen

In general, you need ref-counting only when you _return_ 'ownership'
form a function (which is bad style anyway).

You don't return ownership. shared_ptr owns it's object and you return
a shared_ptr. If you return a shared_ptr from a function, that doesn't
mean that ownership is transferred - after all the function returning
it can still keep a copy.
In other cases
ref-counting is unnecessary. What would be the use of counting?

The question is: Why would you not? What's actually the problem?

As far as I can see, you are trying to make the following points:

1) new/delete asymmetry

2) some point about ownership that I don't fully understand

3) reference counting overhead which is sometimes unnecessary

4) spurious allocation that is sometimes unnecessary

5) additional indirection overhead

For 1, use the wrapper if you take it as that important.

For 2, can you elaborate what you mean?

For 3 - 5, the overhead is fairly small and generally considered worth
the safety. The spurious allocation is also worth the safety, but
could be alleviated by intrusive counting that you will probably also
disapprove of.

Did I miss a point?

One last question: How do you return expensive objects, a
vector, say, from a function?

Regards,

Jens
 
J

Jens Theisen

It's an auto_ptr problem because it violates a basic principle of the
Standard libraries (C and C++):

That a stupid principle anyway. In C, it means that functions have to
return an internal buffer, non-reentrency being the consequence. In
C++, I'd like to draw attention to the problem that deleting
incomplete types don't call destructors. So my advice would rather be:
don't ever use free or delete, and don't make libraries that force
users to do so.

In C++, we have smart pointers to avoid doing so and in C there are
other ways. The APR framework let's you allocate object with respect
to a pool, and all objects are freed when the pool is freed - these
pools again being organised in an hierarchical manner that ususally
corresponds to the stack frame. That leads to a style of C programming
where you again have various allocations, but no explicit
deallocations.
What is 'safer' with shared_ptr. Does it check for NULL pointers
before dereferncing?

Indeed, it might be worth having a variant of shared_ptr that's not
allowed to be null. Good idea. :)
Does it protect you against:
int i = 0;
shared_ptr<int> ap (&i);

That's hardly a mistake you do accidently.
Do 'custom deleters' relly enhance safety?

I don't know. But they are not a central feature anyway.
Is it appropriate for a 'pointer' class (template) which is intended
to replace real pointes to internally allocate a counter for each
pointed-to object? (Real pointes are ultra-lightweight objects, like
an int).

So it's the _name_ you don't like?

Regards,

Jens
 
M

mlimber

Roland said:
It's an auto_ptr problem because it violates a basic principle of the
Standard libraries (C and C++): don't delete (free) what you haven't
allocated. The same appplies to auto_ptr's 'destructive copy
semantics'.

Where in the standard to you find that principle? Most everyone was
originally a little surprised by the destructive copy semantics (but
even that might be addressed in C++0x; see
http://groups.google.com/group/comp.lang.c++/msg/2d7f227fac949c22).
What is 'safer' with shared_ptr. Does it check for NULL pointers
before dereferncing?

The intent of shared_ptr was to be as close as possible to raw
pointers, and that meant no checking overhead (among other things,
e.g., "deep constness" for the pointee such as std::vector supports).
Such a check could have been made optional with a policy-based design
such as that in Loki's smart pointer, but the Boost authors thought
(rightly, IMHO) that doing that would confuse people and hinder
shared_ptr's adoption. If you want checking, there certainly are smart
pointers that support it.
Does it protect you against:
int i = 0;
shared_ptr<int> ap (&i);

No, it didn't intend to, and how could it? You can do the same with raw
pointers:

int i = 0;
int *pi = &i;
delete pi;
Do 'custom deleters' relly enhance safety?

Certainly, because they make shared_ptr easily extensible for other
resources than just raw memory. Thus you get exception safety without
having to hand-roll your own class each time.
Is it appropriate for a 'pointer' class (template) which is intended
to replace real pointes to internally allocate a counter for each
pointed-to object? (Real pointes are ultra-lightweight objects, like
an int).

The answer to this may vary from case to case, but as others have
stated in this thread and elsewhere, usually it is well worth the
(minimal) cost. Sounds to us like you're optimizing prematurely.

Cheers! --M
 
M

mlimber

Roland said:
In general, you need ref-counting only when you _return_ 'ownership'
form a function (which is bad style anyway).

First, you need ref-counting when you share an object among other
objects whose destruction order is indeterminant. It allows you to
easily make sure you don't accidentally delete the object when it is
still needed.

Second, the "bad style" that you're talking about is presumably factory
functions, virtual constructors, and the like. That's a standard design
pattern and well-recieved idiom, not bad style. I know of no one but
you who objects to them. Through this discussion, you cite Stroustrup
as if he's on your side in this. Certainly, he warns against overusing
smart pointers (so do Sutter and Alexandrescu, BTW), but he still
advocates using them, which you do not. Moreover, he advocates the "bad
style" of virtual constructors and factory functions in TC++PL3 15.6.2
and here:

http://www.research.att.com/~bs/bs_faq2.html#virtual-ctor

In short, I don't think you have any real support for your own
home-grown principles of programming. RAII via smart pointers is
well-established, widely used, and recommended and practiced by all
recognized authorities on the language.

Cheers! --M
 
R

Roland Pibinger

One last question: How do you return expensive objects, a
vector, say, from a function?

void populate (std::vector<int>& vec) {
using namespace std;
vector<int> loc;
loc.reserve (1000);
for (int i = 0; i < 1000; ++i) {
loc.push_back (i);
}
vec.swap (loc);
}

I guess that's not smart enough for you. You may prefer something
like:

boost::shared_ptr <std::vector<int> > populate () {
....
}

IMO, that's programing Java in C++.

Best wishes,
Roland Pibinger
 
R

Roland Pibinger

First, you need ref-counting when you share an object among other
objects whose destruction order is indeterminant. It allows you to
easily make sure you don't accidentally delete the object when it is
still needed.

I don't argue against RAII, quite the contrary. Just ref-counting
makes no sense when you don't return the object (any ref-counted
object, not just a shared_ptr).

void foo () {
shared_ptr<int> sp (new int(0));
// assign sp to 100 other shared_ptrs
// pass sp to 100 fuunctions
} // here the ref_count of sp is 0

Second, the "bad style" that you're talking about is presumably factory
functions, virtual constructors, and the like. That's a standard design
pattern and well-recieved idiom, not bad style.

Neither the C++ Standard library nor the C Standard library use your
'standard design pattern'. Guess why?
In short, I don't think you have any real support for your own
home-grown principles of programming. RAII via smart pointers is
well-established, widely used, and recommended and practiced by all
recognized authorities on the language.

If I invented that principles I would be a genius ;-)
Of course, there are various sources that suggest that style of
programming - first and foremost the articulate stack-orientation in
the C++ language itself.

Best wishes,
Roland Pibinger
 
J

Jens Theisen

void populate (std::vector<int>& vec) {
using namespace std;
vector<int> loc;
loc.reserve (1000);
for (int i = 0; i < 1000; ++i) {
loc.push_back (i);
}
vec.swap (loc);
}

I guess that's not smart enough for you. You may prefer something
like:

boost::shared_ptr <std::vector<int> > populate () {
...
}

I indeed would prefer the latter, though I agree, in this example,
it's largely a matter of taste.

However:

How to return an ostream or any other object whiches most derived type
should not be part of the function's signature?

And how do you return expensive-to-copy objects that are not default
constructible?
IMO, that's programing Java in C++.

I could call your way "programming C in C++" as well, though I
appreciate their are different ways with different pros and cons.

Regards,

Jens
 
Z

Zara

Someone made the statement in a newsgroup that most C++ programmers use
smart pointers. His actual phrase was "most of us" but I really don't think
that most C++ programmers use smart pointers, but I just don't know.

I don't like them because I don't trust them. I use new and delete on pure
pointers instead.

Do you use smart pointers?

Most of the time, they are more trustworthy then me.

Zara
 
D

David Harmon

On Sat, 23 Sep 2006 09:00:46 GMT in comp.lang.c++, (e-mail address removed)
(Roland Pibinger) wrote,
What is 'safer' with shared_ptr.

For one thing, it does not feature what you just referred to as
"auto_ptr's 'destructive copy semantics'."
Do 'custom deleters' relly enhance safety?

Maybe, but mainly that's part of the "more flexible behavior"
that I mentioned.
Is it appropriate for a 'pointer' class (template) which is intended
to replace real pointes to internally allocate a counter for each
pointed-to object? (Real pointes are ultra-lightweight objects, like
an int).

Yes, it's appropriate. No, they don't replace all simple pointers.
Simple pointers are still appropriate for lightweight uses and where
they don't "own" anything.
 
D

David Harmon

On Sat, 23 Sep 2006 10:11:48 +0100 in comp.lang.c++, "Stuart
Golodetz said:
:) To quote from my previous post, "* Making things exception-safe is harder
with raw pointers." I think I should have made it more visible, I squeezed
it in at the bottom of the list.

Maybe you should use a list-in-first-out list.
 
M

mlimber

Roland said:
I don't argue against RAII, quite the contrary. Just ref-counting
makes no sense when you don't return the object (any ref-counted
object, not just a shared_ptr).

void foo () {
shared_ptr<int> sp (new int(0));
// assign sp to 100 other shared_ptrs
// pass sp to 100 fuunctions
} // here the ref_count of sp is 0

How about:

class A { ... };
class B { ... };

void Bar( A& a, B& b )
{
shared_ptr<int> sp( new int(0) );
// ...
a.Use( sp );
b.Use( sp );
// sp is destroyed unless a or b retained a reference to it
}
If I invented that principles I would be a genius ;-)
Of course, there are various sources that suggest that style of
programming - first and foremost the articulate stack-orientation in
the C++ language itself.

Your innovation is restricting yourself to using RAII without using
smart pointers, which the standard library provides explicitly for that
purpose.

Cheers! --M
 
L

loufoque

Roland Pibinger wrote :
By value, of course.
Returning a pointer is an example of how people ridiculously use dynamic
allocation inappropriately.
void populate (std::vector<int>& vec) {
using namespace std;
vector<int> loc;
loc.reserve (1000);
for (int i = 0; i < 1000; ++i) {
loc.push_back (i);
}
vec.swap (loc);
}

Or simply

std::vector<int> populate()
{
using namespace std;
vector<int> loc;
loc.reserve (1000);
for (int i = 0; i < 1000; ++i) {
loc.push_back (i);
}
return loc;
}

which has the same cost than a swap with move semantics and is even more
efficient with NRVO (available on any decent compiler)
 
S

Stuart Golodetz

David Harmon said:
On Sat, 23 Sep 2006 10:11:48 +0100 in comp.lang.c++, "Stuart


Maybe you should use a list-in-first-out list.

Oh dear :) Programmer humour alert!
 
L

loufoque

Jim Langston wrote :
Do you use smart pointers?

Not really, since I hardly even use dynamic allocation outside of
private parts of a class.
I especially avoid refcounting ones like shared_ptr, even though I have
nothing against objects using refcounting under the hood if they still
satisfy value semantics.

shared_ptr is used for many reasons, from which there are :
- Java-ish polymorphism heavy designs
- Resource management for objects which don't really have a
deterministic lifetime
- Avoidance of the overhead of copy semantics

For all of these usages, we can obviously see that shared_ptr is often
not the right solution.

Avoiding the overhead of copy semantics should be done by appropriate
use of references, by the compiler optimizing out temporaries, by the
usage of swap functions when relevant, and by move semantics, the new
feature introduced in C++0x, which will, hopefully, bring a great plus.

Objects that don't have a deterministic lifetime should use garbage
collection. Even though some people want garbage collection just because
they can't design software well, there are also some valid uses for it.
Shared_ptr is nothing but a poor replacement.

And for polymorphism, be it reasonable use or overuse ala Java (and
java-ish designs also often expect garbage collection), others tools
such as references, scoped_ptr, move_ptr or clone_ptr seem more fit.
clone_ptr should be usable for most things (and you could even use one
that uses refcounting under the hood) even though I think a smart
polymorphism object (and not pointer) is even better since it provides
more appropriate semantics.
 
J

Jens Theisen

loufoque said:
Roland Pibinger wrote :

By value, of course.
Returning a pointer is an example of how people ridiculously use
dynamic allocation inappropriately.

That's only possible in the special case where you construct the
object in the function returning it. If it's already there (accessed
through some of the parameters) and returned as-is, it will be copied.

I appreciate that it's not always the optimal solution. The OP,
however, didn't ask: Are smart pointers (or even _a_ particular smart
pointer) the answer to all problems, he asked if they are generally
used.

In this case, returning a vector rellying on NRVO is probably the best
thing to do, but returning by auto_ptr is also decent. Returning by
shared_ptr is still decent.

The only cost is a small runtime overhead, and please mind that
premature optimisation is the root of all evil. Also mind that not all
people have the time to make themselves familiar with all alternatives
there are. If you had to make compromises in what to make them
familiar with, would you compromise NRVO or shared_ptr?

shared_ptr is something that should be in the first chapter of any C++
tutorial.
which has the same cost than a swap with move semantics and is even
more efficient with NRVO (available on any decent compiler)

As a side note, shared_ptr will also benefit from move semantics,
won't it? At least I can't see a need for changing the refcount on
returning an rvalue shared_ptr anymore. That's not meant to be as an
argument in your specific example, where you were worrying about the
heap allocation cost.

Regards,

Jens
 
R

Roland Pibinger

That's only possible in the special case where you construct the
object in the function returning it. If it's already there (accessed
through some of the parameters) and returned as-is, it will be copied.

In that special case there is even a workaround ...

// pray for RVO (or look up the compiler switch)
std::vector<int> populate () {
...
}

int main() {
std::vector<int> v;
populate().swap (v);
}

That's certainly not what I consider a good programming style. It
relies on a hack (RVO) and programs more to the implemenation than to
the interface.

Best wishes,
Roland Pibinger
 
F

Frederick Gotham

loufoque posted:
Returning a pointer is an example of how people ridiculously use dynamic
allocation inappropriately.

The following code works perfectly:

#include <cstddef>
#include <cstring>
#include <cassert>

char *const PrependDriveLetter(char const letter, char const *const dir)
{
assert(dir);

std::size_t const len = std::strlen(dir);

char *const p = new char[len+3+1];

p[0] = letter; p[1] = ':'; p[2] = '\\';

memcpy(p+3,dir,len+1);

return p;
}

#include <iostream>
int main()
{
char const *const p = PrependDriveLetter('C',"Music\\Albums");

std::cout << p << '\n';

delete [] p;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top