views:

324

answers:

2

I'm having trouble storing instances of my smart pointer into a container. Here is the code for the pointer.

#include "std_lib_facilities.h"

template <class T>
class counted_ptr{
private:
    T* pointer;
    int* count;

public:
    counted_ptr(T* p = 0, int* c = new int(1)) : pointer(p), count(c) {}    // default constructor
    explicit counted_ptr(const counted_ptr& p) : pointer(p.pointer), count(p.count) { ++*count; } // copy constructor
    ~counted_ptr()
    {
        --*count;
        if(!*count) {
            delete pointer;
            delete count;
        }
    }

    counted_ptr& operator=(const counted_ptr& p)    // copy assignment
    {
        pointer = p.pointer;
        count = p.count;
        ++*count;
        return *this;
    }
    T* operator->() const{ return pointer; }
    T& operator*() const { return *pointer; }
    int& operator[](int index) { return pointer[index]; }

    int Get_count() const { return *count; }    // public accessor for count


};




int main()
{
    counted_ptr<double>one;
    counted_ptr<double>two(one);
    one = new double(5);
    vector<counted_ptr<double> >test;
}

In int main(), the vector<counted_ptr<double> > line does compile. When I first tried it with just vector<counted_ptr<double> > it didn't compile (probably because it was lacking parameters.) However, when I try to use push_back such as

test.push_back(one);

I get a compiler error that opens up vector.tcc with the specific error saying that

no matching function for call to `counted_ptr<double>::counted_ptr(const counted_ptr<double>&)'|

I'm guessing that push_back can't find a counted_ptr, but I'm really not sure. Any help is appreciated, thanks.

Edit: However, this works. test[0] = one; I guess the semantics of push_back are what is restricting it.

+5  A: 

You may want to try this:

test.push_back(counted_ptr<double>(one));

You copy constructor is explicit which means that the compiler won't implicitly invoke it.

Personally, I would make the raw pointer constructor explicit and the copy ctor not explicit. That would be closer to usual behavior.

EDIT: I also recommend that you implement a swap method. It makes assignment absolutely trivial. You end up with something like this:

counted_ptr &operator=(const counted_ptr &rhs) {
    counted_ptr(rhs).swap(*this);
    return *this;
}

This also has the benefit of all of the accounting happening in constructors/destructors which is a lot simpler to manage :-).

Evan Teran
The swap implementation is wrong. You have to copy and swap rather than just swap. Otherwise you modify the item on the right of the assignment ;)
Billy ONeal
You are mistaken. `counted_ptr(rhs)` makes a copy, that that copy is swapped with the contents of `*this`.
Evan Teran
Unless you meant this, you would have to write a swap member function first, and then use the assignment operator that way. Without it you can't call swap like that.
trikker
@Billy. No this is correct. It caught me out at first (Which is why I don't like it), But this creates a temporary variable (by copying rhs) then swaps this with this. @trikker. It is pretty common practice to have your class implement swap() (it is used all over the STL to optimize stuff) so it is valid assumption that you can easily write one without having to be told what to do.
Martin York
I just used called swap with the normal syntax swap(a,b)
trikker
Making the copy constructor not explicit allowed push_back to work. I'll definitely look more into explicit so I understand it better.
trikker
+2  A: 

Your assignment operator is wrong.
What happened to the object it was pointing at?
What happens if you are assigning to yourself or the same internal object?

counted_ptr& operator=(const counted_ptr& p)    // copy assignment
{
    if (&p != this)
    {
        --*count;
        if ((*count) == 0) // Much more readable than !*count
        {
            delete pointer;
            delete count;
        }
        pointer = p.pointer;
        count = p.count;
        ++*count;
    }
    return *this;
}

Note: Writing your own smart pointer is not a good idea. They are not as trivial to write as you think.

Note: This is the first thing I spotted. There could be more, and I am not sure this is 100% correct.

In fact I would change the assignment operator to use copy/swap idium.

counted_ptr& operator=(const counted_ptr& p)    // copy assignment
{
    counted_ptr<T>     tmp(p);   // increment counter on p
    swap(tmp.pointer, pointer);
    swap(tmp.count    count);

    return *this;
                                 // tmp goes out of scope and thus the
                                 // destructor gets called and what was in this
                                 // object is now destroyed correctly.
}
// It may even be worth writing your own swap operator.
// Make sure you declare it as no-throw and grantee it.
Martin York
+1 for not writing your own smart pointer class.
Andy
I'm doing it for an exercise. Obviously if I was writing real code I would use auto_ptr or one of the smart pointers from boost. I will try this out though.
trikker
did you intend for `tmp` to be a reference? That seems broken...
Evan Teran
@Evan: Oops. Fixed.
Martin York
Thanks for the info on fixing my copy assignment. However, I still can't store a counted_ptr in a vector. Even after trying Evan's method.
trikker