views:

3628

answers:

5

What's the equivalent to the following:

std::vector<Foo*> vec;
vec.push_back(NULL);

when dealing with boost::shared_ptr? Is it the following code?

std::vector< boost::shared_ptr<Foo> > vec;
vec.push_back(boost::shared_ptr<Foo>());

Note: I may push back a lot of such objects. Should I declare a global static nullPtr object somewhere? That way only one of them would have to be constructed:

boost::shared_ptr<Foo> nullPtr;
A: 

Yes, declare a global static null pointer.

Christopher Dolan
+3  A: 

You could declare a global nullPtr for shared_ptr<Foo>. But if you pollute the global namespace, what would you call the global nullPtr for shared_ptr<Bar>?

Typically I declare the null ptr as a static in the class of the pointer.

#include <boost\shared_ptr.hpp>

class Foo; // forward decl
typedef boost::shared_ptr<Foo> FooPtr;
class Foo
{
public:
    static FooPtr Null;
}
...
// define static in cpp file
FooPtr Foo::Null;
...
// use Foo Null
vec.push_back(Foo::Null);

That way each class has a static Null.

m-sharp
You can get a more natural syntax by using a templated conversion operator on a global variable. <plug>See my answer...</plug> :)
j_random_hacker
+13  A: 

Your suggestion (calling the shared_ptr<T> constructor with no argument) is correct. (Calling the constructor with the value 0 is equivalent.) I don't think that this would be any slower than calling vec.push_back() with a pre-existing shared_ptr<T>, since construction is required in both cases (either direct construction or copy-construction).

But if you want "nicer" syntax, you could try the following code:

class {
public:
    template<typename T>
    operator shared_ptr<T>() { return shared_ptr<T>(); }
} nullPtr;

This declares a single global object nullPtr, which enables the following natural syntax:

shared_ptr<int> pi(new int(42));
shared_ptr<SomeArbitraryType> psat(new SomeArbitraryType("foonly"));

...

pi = nullPtr;
psat = nullPtr;

Note that if you use this in multiple translation units (source files), you'll need to give the class a name (e.g. _shared_null_ptr_type), move the definition of the nullPtr object to a separate .cpp file, and add extern declarations in the header file where the class is defined.

j_random_hacker
nice idea dude. i had to +1 that :p but note the answer over here http://stackoverflow.com/questions/395685/how-do-you-choose-between-a-singleton-and-an-unnamed-class/395738#395738
Johannes Schaub - litb
Thanks guys. :) Looking at litb's post suggests that the class should really be named though.
j_random_hacker
+1 for evil c++ hackage
JaredPar
some silly names: nullPtrT, NullPtrType, nullPtr_t . whatever :) i found they append a _t when there is only one instance of something (like, nothrow_t and nullptr_t).
Johannes Schaub - litb
another idea what to do when different TUs are involved: for avoiding static initialization fiasco you could also create a nullPtr() function, that returns an object of your null class. so you don't run into initialization order problems when something of a different TU uses your null object.
Johannes Schaub - litb
@litb: *In practice* the fiasco isn't a problem here, since the only thing that's ever called is the conversion operator, and that doesn't care if the object has been initialised or not. Alternatively you could even have per-TU static instances. (Just looking for ways to avoid function-call syntax!)
j_random_hacker
r_random_hacker, oh right. i forgot that it isn't the actual shared_ptr. yeah in practice there are probably no problems regarding that fiasco. i even think it is a POD so it is not even a problem theoretical (so it has no dynamic initialization). so never mentioned my "idea" before. silly litb :)
Johannes Schaub - litb
@litb: Yeah I guess it actually is a POD! A pretty strange one mind you... :)
j_random_hacker
@j_random_hacker: Extremely clever and kinda cool, but doesn't this seem like going a bit overboard? There's a much simpler and cleaner solution just using `typedef`. See my answer below.
Robert S. Barnes
@Robert: Thanks, maybe it is going overboard. But I like that it works for any type.
j_random_hacker
+1  A: 

Well, this is legal:

shared_ptr<Foo> foo;  /* don't assign */

And in this state, it doesn't point to anything. You can even test this property:

if (foo) {
    // it points to something
} else {
    // no it doesn't
}

So why not do this:

std::vector < shared_ptr<Foo> > vec;
vec.push_back (shared_ptr<Foo>);   // push an unassigned one
Larry Gritz
+1  A: 

Here's something which I think is a bit simpler and works just fine

( remember that typedef is your friend ):

#include    <cstdlib>
#include    <vector>
#include    <iostream>
#include    <boost/shared_ptr.hpp>

typedef boost::shared_ptr< std::vector<char> > CharVecHandle;

inline CharVecHandle newCharVec(std::vector<char>::size_type size) {
    return CharVecHandle(new std::vector<char>(size));
}

inline CharVecHandle newCharVec(void) {
    return CharVecHandle();
}

int main ( void )
{
    CharVecHandle cvh = newCharVec();

    if (cvh == NULL) 
        std::cout << "It's NULL" << std::endl;
    else 
        std::cout << "It's not NULL" << std::endl;

    std::vector< CharVecHandle > cvh_vec;

    cvh_vec.push_back(newCharVec(64));
    cvh_vec.push_back(newCharVec());

    // or call the NULL constructor directly
    cvh_vec.push_back(CharVecHandle());

    return EXIT_SUCCESS;
}
Robert S. Barnes
Sure, a `typedef` is nicer to look at than `boost::shared_ptr<Foo>`, and the OP seems to be concerned only with a single type `Foo`, in which case `typedef` fits nicely. If you want to avoid a proliferation of `typedef`s for different smart pointer types, my approach is better I think. Not sure what you're trying to demonstrate with your `newCharVec()` functions though, can you explain?
j_random_hacker
@j_random_hacker: The purpose of the `newCharVec` functions is the same as the typedef, convenience and consistency. Less characters to type and consistency in creating real objects and NULL objects. The thing is, that even if I was using the template method, I would still probably create a typedef. Coming from a `C` background whenever I see an overly complicated type declaration my first instinct is to typedef it. Taking that into account, I'm not sure if there's really an objective reason to pick one method or the other, it may be an issue of style or personal preference.
Robert S. Barnes
Well, one reason to prefer my approach is that, because my `nullPtr` is type-agnostic, it will work in function templates when manipulating variables whose type is unknown, while yours won't. E.g. I can write a function template `template <typename T> push_a_null_ptr(vector<shared_ptr<T> > }`, which will work for all types `T`. I admit it's not a huge upside, but I don't see any downsides.
j_random_hacker
Um, maybe I misinterpreted your last comment -- I thought you were comparing your approach to my approach, but maybe you were really comparing your approach to the OP's "starting point" approach of just spelling out the full `shared_ptr<Foo>()` each time? If the latter then I agree your approach is nicer, I'd rather look at a short `typedef`ed name than a long template instantiation. But I still think mine's *even* better... :)
j_random_hacker