Skip to main content
GameDev.net gamedev.net
🔒 Locked

Noob needs help with cloning

Started by Mozly Apr 2, 2011 at 1:11 AM 22 replies 3.7k views
Original Post
Mozly
Mozly
I'm having a problem with copying vectors. So I have this vector of objects call it std:: vector first_vector. parent is the base class. The problem is I want to create a seperate vector std::vector second_vector that gets all the information copied from first_vector.

The problem is when I copy it like this

second_vector = first_vector;

it copies only the adress of the pointers in first_vector so when I change one it changes the other. I needed to know How do I deep copy vector with pointers such that they are independant

So I did some more searching and found This aricle expalining how to clone an object. I followed the directives exactly since I need polymorphic copying. so now I am copying like this

second_vector.resize(first_vector.size());

for(unsigned i = 0; i < first_vector.size(); ++i)
second_vector = first_vector->clone();


But now I get this error:
c:\program files\microsoft visual studio 10.0\vc\include\sstream(724): error C2248: 'std::basic_ios<_Elem,_Traits>::basic_ios' : cannot access private member declared in class 'std::basic_ios<_Elem,_Traits>'
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits
1> ]
1> c:\program files\microsoft visual studio 10.0\vc\include\ios(176) : see declaration of 'std::basic_ios<_Elem,_Traits>::basic_ios'
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits
1> ]
1> This diagnostic occurred in the compiler generated function 'std::basic_stringstream<_Elem,_Traits,_Alloc>::basic_stringstream(const std::basic_stringstream<_Elem,_Traits,_Alloc> &)'
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits,
1> _Alloc=std::allocator
1> ]


I suspect it is because the objects in my vector contain string stream. My question is how do I fix this? How do I clone objects with string stream
Bregma
Bregma
  1. Does your clone() function construct a new object?
  2. Does your constructor use initializer lists when constructing members?
  3. Do you have a copy constructor and have you followed the Rule of Three?
Stephen M. Webb
Professional Free Software Developer
Mozly
Mozly
Here is the constructor

struct clonable {
virtual ~clonable() {}
virtual clonable* clone() const = 0;
};

class Base : public clonable {
public:
virtual Base* clone() const
{ return new Base( *this ); }
};

class Derived : public Base {
public:
virtual Derived* clone() const
{ return new Derived( *this ); }
};



1 - yes
2 - I dont know what that means
3 - I especially dont know the rule of three
Zakwayda
Zakwayda
2 - I dont know what that means[/quote]
Google/search for 'c++ initializer list'.

3 - I especially dont know the rule of three[/quote]
Google/search for 'c++ rule of three'.
SriLumpa
SriLumpa

Here is the constructor


You are probably not showing the problematic code, because there is no mention of stream here.
Hodgman
Hodgman

You are [s]probably[/s] [definitely] not showing the problematic code, because there is no mention of stream here.
Yep, that code doesn't reproduce the error in the first post, which is probably caused by the real (non-posted) constructor trying to copy a stream.

When you trim down code to include in a forum post, try to ensure the trimmed down version still exhibits the problem at hand.

What kind of streams are you using? Googling "how to copy a stringstream" brought up this: copy from one stringstream object to another in C++
Mozly
Mozly
ok first

  • Does your clone() function construct a new object?
  • Does your constructor use initializer lists when constructing members?
  • Do you have a copy constructor and have you followed the Rule of Three?
    [/quote]

    I google searched it and yes to all three

    Second, I realize I need to be more specific with my code because the previous snippet that i put wasn't my own code but a generic version of what i was doing, So here is the snippet from my code:


    struct cloneable
    {
    virtual ~cloneable(){}
    virtual cloneable* clone() const = 0;
    };

    class Digit: public cloneable
    {
    protected:
    //x and y positons
    int Xpos;
    int Ypos;

    //value in string format
    std::stringstream svalue;

    public:
    Digit(int x, int y);
    ~Digit();
    //copy constructor
    virtual Digit* clone() const
    { return new Digit( *this ); }
    };


    class Realnum: public Digit
    {
    protected:
    float value;
    public:
    Realnum();
    ~Realnum();

    virtual Realnum* clone() const
    {return new Realnum( *this);}
    };
Zakwayda
Zakwayda
Maybe this has already been covered, but why do you have a stringstream object as a member variable? Why not just store a string?
Bregma
Bregma
The code you are posting does not provide a copy constructor, it is using the default copy constructor which probably does not do what you want.

Like the google results say, a copy constructor looks something like this.
[source lang='cpp']
class Digit
{
public:
Digit(const Digit &rhs)
: svalue(rhs.svalue)
{ }

private:
std::stringstream svalue;
}[/source]
Stephen M. Webb
Professional Free Software Developer
Mozly
Mozly

Maybe this has already been covered, but why do you have a stringstream object as a member variable? Why not just store a string?


I need to store int, double, float and char into a string and there is no better way than string stream
Zakwayda
Zakwayda

I need to store int, double, float and char into a string

Right, but why store the stringstream itself? Why not just store a string?

(I'm sure there are exceptions, but generally you don't want to store stream objects in that way. What would probably make more sense would be to convert the value to a string, and then simply store the string.)
Mozly
Mozly

[quote name='Mozly' timestamp='1301753304' post='4793485']
I need to store int, double, float and char into a string

Right, but why store the stringstream itself? Why not just store a string?

(I'm sure there are exceptions, but generally you don't want to store stream objects in that way. What would probably make more sense would be to convert the value to a string, and then simply store the string.)
[/quote]
In the future I think I'll do just that so I dont have to deal with this mess but now my code is too big for me to go one by one trying to change all the string stream values

@ Bregma, Ok so I made a copy constructor but the compiler doesn't call the correct constructor here is what I mean. My classes look like this now


struct cloneable
{
virtual ~cloneable(){}
virtual cloneable* clone() const = 0;
};

class Digit: public cloneable
{
protected:
//x and y positons
int Xpos;
int Ypos;

//value in string format
std::stringstream svalue;

public:
Digit(int x, int y);
Digit(const Digit ©) :svalue(copy.svalue)
{}

~Digit();

};


class Realnum: public Digit
{
protected:
float value;
public:
Realnum( int x, int y);
Realnum(const Realnum ©): Digit(copy)
{}
~Realnum();
};



I copy it like this in my main function


undo_vector.resize(equation_buffer.size());
for(int i=0; i< equation_buffer.size(); i++)
{


undo_vector.at(i) = new Realnum(equation_buffer);


}


but I get his error:

error C2664: 'Realnum::Realnum(int,int)' : cannot convert parameter 1 from 'Digit *' to 'float'
1> There is no context in which this conversion is possible

why doesen't it call the Realnum(const Realnum ©) constructor?
Hodgman
Hodgman
why doesen't it call the Realnum(const Realnum ©) constructor?
Because you're passing a "[font="Lucida Console"]const Realnum *[/font]" instead of a "[font="Lucida Console"]const Realnum &[/font]".The difference between the following two snippets, is that the first one is dereferencing a pointer before passing it to the constructor, while the second is passing in the pointer directly. You need to add the '*' to the second snippet in order to dereference that pointer.//#1
virtual Digit* clone() const { return new Digit( *this ); }
//#2
undo_vector.at(i) = new Realnum(equation_buffer);

[font="arial, verdana, tahoma, sans-serif"]The code you are posting does not provide a copy constructor, it is using the default copy constructor which probably does not do what you want.
*snip*
That example does the same thing that the default copy-constructor does... [/font]He needs to use the method for copying stringstreams that was posted earler. class Digit
{
public:
Digit(const Digit &rhs)
{
svalue << rhs.svalue.rdbuf(); // copy everything inside rhs's stream to the new stream
}

private:
std::stringstream svalue;
}
Mozly
Mozly

[quote name='Mozly' timestamp='1301754344' post='4793493']why doesen't it call the Realnum(const Realnum ©) constructor?
Because you're passing a "[font="Lucida Console"]const Realnum *[/font]" instead of a "[font="Lucida Console"]const Realnum &[/font]".The difference between the following two snippets, is that the first one is dereferencing a pointer before passing it to the constructor, while the second is passing in the pointer directly. You need to add the '*' to the second snippet in order to dereference that pointer.//#1
virtual Digit* clone() const { return new Digit( *this ); }
//#2
undo_vector.at(i) = new Realnum(equation_buffer);

[font="arial, verdana, tahoma, sans-serif"]The code you are posting does not provide a copy constructor, it is using the default copy constructor which probably does not do what you want.
*snip*
That example does the same thing that the default copy-constructor does... [/font]He needs to use the method for copying stringstreams that was posted earler. class Digit
{
public:
Digit(const Digit &rhs)
{
svalue << rhs.svalue.rdbuf(); // copy everything inside rhs's stream to the new stream
}

private:
std::stringstream svalue;
}

[/quote]

Part 2 of your answer solved the string stream problem for me but I'm still stuck trying to copy because I cant add '*' to the second snippet because it gives me some error in other parts of my code when I initialize Realnum telling me that I have an invalid call to a constructor.

So what I did instead is left the copy constructor as is but changed the way I copy using a copy function as previously done. It looks something like this now:


struct cloneable
{
virtual ~cloneable(){}
virtual cloneable* clone() const = 0;
};

class Digit: public cloneable
{
protected:
//x and y positons
int Xpos;
int Ypos;

//value in string format
std::stringstream svalue;

public:
Digit();
Digit(const Digit ©)
{copy.svalue.rdbuf();}

~Digit();

//copy fucntion
virtual Digit* clone() const
{ return new Digit( *this ); }

};


class Realnum: public Digit
{
protected:
float value;
public:
Realnum( int x, int y);
Realnum(const Realnum ©): Digit(copy)
{}
~Realnum();

//copy fucntion
Realnum* clone() const
{return new Realnum( *this);}
};


my copying method now looks like this

for(int i=0; i< equation_buffer.size(); i++)
{
undo_vector = equation_buffer->clone();

}


Now my program compiles without error but I have a problem at runtime. The problem now is that the clone() returns something that does not initialize the values in undo_vector
It returns this:
Realnum::clone returned 0x007d7660 {value=-4.3160208e+008 order=-842150451 } Realnum *

where is should return something more like
Realnum::clone returned 0x007d7660 {value=-2 order=0 } Realnum *
Mozly
Mozly
still dont have it
SiCrane
SiCrane
Your copy constructors don't copy all your member variables.
Mozly
Mozly
I understand that but how do I solve the problem
SiCrane
SiCrane
By copying the member variables that you presently don't copy.
Mozly
Mozly
does that mean that I would have to change my clone() function?
SiCrane
SiCrane

Your copy constructors don't copy all your member variables.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.