views:

102

answers:

4

I have a class Model:

class Model
{
    ...

    boost::shared_ptr<Deck>  _deck;
    boost::shared_ptr<CardStack> _stack[22];
};

Deck inherits from CardStack.

I tried to make _stack[0] point to the same thing that _deck points to by going:

{
    _deck = boost::shared_ptr<Deck>(new Deck());
    _stack[0] = _deck;
}

It seems that the assignment to _deck of _stack[0] results in a copy of _deck being made. (I know this because modifications to _stack[0] do not result in modifications to _deck.) How can I get them to point to the same thing?

Ok - no copy constructor is being called. I have verified this by implementing it and seeing if it gets called - it doesn't.

However - I have a function that operates on CardStack objects:

void TransferSingleCard(CardStack & src, CardStack & dst, Face f)
{
    if( !src._cards.empty() )
    {
        src._cards.back().SetFace(f);
        dst.PushCard(src._cards.back());
        src._cards.pop_back();
    }   

}

Now - when I call:

{
    TransferSingleCard(*_stack[DECK], _someotherplace, FACEDOWN);
    std::cout << *_stack[DECK];
    std::cout << *_deck;
}

I get this output (where std::cout on a CardStack will print out the size of that stack):

Num(103) TOP
Num(104) TOP

... so I've concluded (incorrectly?) that _stack[DECK] points to something different.

The Deck

class Deck : public CardStack
{
public:
    Deck(int numsuits=2, StackIndex index = NO_SUCH_STACK );
    Deck::Deck( const Deck & d);
    int DealsLeft() const;
    void RecalcDealsLeft();
private:
    int _dealsleft;
};
+2  A: 

Not clear what you are asking about - consider this code:

#include <iostream>
#include "boost/shared_ptr.hpp"
using namespace std;

struct A {
    virtual ~A() {
        cout << "destroyed" << endl;
    }
};

struct B : public A {
};

int main() {
    boost::shared_ptr<B> b( new B );
    boost::shared_ptr<A> a;
    a = b;
}

Only one "destroy" message appears, indicating that no copy has been made.

anon
+1  A: 

I think the problem is that you're assigning between different types here. boost::shared_ptr is a template and templates are not polymorphic even if the type in them is. So what's happening is that your compiler sees the assignment from boost::shared_ptr<Deck> to boost::shared_ptr<CardStack> and notices that it can make the assignment by calling the copy constructor for CardStack to duplicate the Deck object.

I think what you want the assignment to look like is something like this:

_stack[0] = boost::static_pointer_cast<CardStack>(_deck);

Which will do the conversion the way you expect it to.

Timo Geusch
I tried your solution, but no joy.
BeeBand
@BeeBand - I just noticed you updated the question and you've verified that it doesn't call the copy constructor. In that case the above technique won't help.
Timo Geusch
+1  A: 

This example - derives from @Neil's answer, tries to emulate what you say is happening. Could you check that it works as expected (A and B have the same count) on your system.

Then we could try and modify this code or your code until they match.

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

class A {
    public:
    virtual ~A()
    {
        std::cerr << "Delete A" << std::endl;
    }
    int _count;
    void decrement()
    {
        _count --;
    }
};
class B : public A {
    public:
    virtual ~B()
    {
        std::cerr << "Delete B" << std::endl;
    }
};

int main()
{
    boost::shared_ptr<B> b(new B);
    b->_count = 104;
    boost::shared_ptr<A> a;
    a = b;

    a->decrement();

    std::cerr << "A:" << a->_count << std::endl;
    std::cerr << "B:" << b->_count << std::endl;
    return 0;
}

EDIT:

So from the comment, we know the original pointers are correct, so now we need to trace.

Either:

  1. log pointers to see when they change.
  2. Use watchpoints in a debugger to see when the pointer changes.
  3. Use a third shared pointer to see which pointer is changed.
  4. Introduce a function that changes both pointers at the same time.
Douglas Leeder
Well, I went back to basics and your code proves that they do indeed *start off* pointing to the same thing. The `TransferSingleCard()` even works - the size is the same. However, further down the line, the `TransferSingleCard()` gives me different results for each pointer.
BeeBand
I guess that means that one of the pointers is getting changed - I suggest logging the pointer address each time you print out the size, and see when they get changed.
Douglas Leeder
Yes you're right. The `_deck` pointer was getting reset to something else via `boost::shared_ptr::reset()`. I think I had assumed that `reset()` wouldn't change the address of `_deck` (not sure why in retrospect), and that `_stack[DECK]` would not have to change also. Anyway - thanks.
BeeBand
+1  A: 

I think you may want shared_array for _stack . . . Take a look at the documentation on shared_ptr;from boost.org, specifically:

http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm

"Normally, a shared_ptr cannot correctly hold a pointer to a dynamically allocated array. See shared_array for that usage."

Also, be aware of the T* get() function (not to be used without good reason) which returns the raw pointer being held by the managed pointer (shared_ptr in this case).

bn
`CardStack` is basically a wrapper around a `std::deque<Card>`. Does `shared_array` still apply?
BeeBand
It does if you have a shared_ptr to an array in your code. I may have misunderstood . . .
bn