Deep-copying a structure containing reference members (C++) - c++

I have a C++ class that contains objects that have reference members pointing to other objects within the class. This seemed like a good idea at the time, but now I need to implement a deep copy of the whole thing, and I can't see a way to do that that doesn't feel like a clunky hack.
A simplified version of my code looks like this. The question is about writing a copy constructor for A.
class C {
int x, y, z; // nothing complicated stored in this class
public:
// constructor and other methods
};
class B {
C &c1;
C &c2;
public:
// constructor and other methods
};
class A {
C *c_array;
B *b_array; // for each item in this array,
// its 'c1' and 'c2' fields point to members of c_array.
public:
// constructor and other methods
};
A few people have asked how this structure is initialised, so please let me stress that this is irrelevant for answering the question. The initialisation will always satisfy the requirement that the reference members of the items in b_array point to items in c_array, but beyond that the data could be anything. It is important that the copy constructor work for any data that satisfies this property. This is not a problem that can be solved by reusing the existing initialisation code.
The problem is that if I just copy the objects in b_array, their reference members will point to the C objects in the old instance of A. I need to make them point to the corresponding items in the new instance. The only way I can think to do that is this:
for each element of b_array, get the address that its reference member points to, and store that in a pointer
work out the index into the array that that pointer corresponds to using pointer arithmetic
use this index to initialise the reference member of the corresponding element of the new b_array.
My question is, is there a cleaner / simpler / more elegant way? If there isn't, I will just refactor my design to use array indices instead of references throughout.
Perhaps I shouldn't have used reference members - I know some people say it's always better to use pointers. If I had used pointers instead of references, would there be a better solution to this problem? (I can't see one but I don't know.)

A deep copy using references will have to first copy the values (c_array) and then store references to those new values. I cannot see a way of achieving this other than the algorithm you describe.
Using pointers instead of references will not change this. There are various comparisons of pointers vs references. Pointers are less tricky to initialise (create as null and assign when you are ready) and more dangerous to use (might still be null). But you will still have to copy the objects, then find + copy the links to them.
I cannot see a simpler / more elegant way than using array indices. With array indices you will just copy the arrays value by value, and the structure of which index points to which object will be taken care of for you.

You can provide an assignment operator (and a copy ctor) for A that deals with the change of both c_array and b_array in tandem, assuming that B can handle the assignment of C as just a reference/pointer update.
It can be along the lines of:
struct B { ... B& operator=(const C& c) { this->c = &c; return *this; } };
struct A { ...
A& operator=(const A& a) {
c_array = a.c_array;
b_array = a.b_array;
// re-assign pointers/references only using B's `operator=(const C&)` :
std::copy_n(c_array.begin(), c_array.size(), b_array.begin());
return *this;
}
};
A bit messy, but: see live example
Note that if you comment out the std::copy_n line, you can of course observe in the output that the copy isn't detached, that is, the b_array of the copy points to the original's c_array instead of its own.

Related

Writing a copy constructor for a class in C++ that has a set containing pointers

Say you had a class in C++ like below:
template<typename T>
class my_class {
public:
private:
std::set<T*> class_set;
int elements_in_set;
}
I'm trying to write a copy constructor for this class, and so far I'm stuck at the following, where I only know how to copy over the integer elements_in_set, but I'm not sure how to copy over the set, and do a deep copy of the things that other's class_set's pointers are pointing to:
my_class(my_class const& other)
: class_set{}, elements_in_set{other.elements_in_set} {
//don't know how to copy over the set
}
I know that if the class has a pointer as a member, you'd use new data type[size of data pointer points to], but do you have to call new to make the set itself, and then new for every pointer in the set? Any help would be greatly appreciated. It would also be nice if there was a way to do this using standard library algorithms rather than for loops.
Depends. Do you want a shallow or a deep copy?
For a shallow copy (both instances of my_class sharing the same T objects) just copy the set in the same way as the int member.
For a deep copy (the new instances gets its own copy of the T objects) you need to run through the original set, create a new T and insert it into the new set. If it is just T objects in the set using new will do the job. If T is a base-class you will have to add a clone function.
Note that having a pointer in a set is at least slightly unusual. If you want the second behaviour (deep copy) using values instead of pointers would give you that for free.
Edit: Note that your int member is redundant. A set keeps track of its size.

What happens when using pass by reference and member initialization lists in C++ classes?

Okay, so I have a large vector say
vector<vector<vector<int>>>
of 10000 by 10000 by 10000.
I have a class which has such a vector as a private member variable:
class foo {
private:
vector<vector<vector<int>>> myvector
};
I have a constructor for my class that uses pass by reference and initializer list:
foo(vector<vector<vector<int>>> &myvector_in) : myvector(myvector_in);
I want to know what's exactly happening in terms of memory usage. Is the private myvector the same as the one that was originally declared, or is it a copy.
Basically, I want to know if there are ever two version of myvector in memory.
Thank You!
Here is a fishing tip.
Fairly easy to answer yourself. Set [0][0][0] of myvector_in to a known value. Invoke the constructor and inside it also set [0][0][0] but of myvector to a different value. Once the constructor has returned, print the content of myvector_in. If it's the same as the one you original set you must conclude that the two vectors are different entities, thus one was copied into a different one. If they are the same than you can conclude they are in fact the same instances.
You could also print addresses to get a better sense of what's what.
I must point out, the memory requirement mention in your original question are in the realm of super computer, you got one?
You have a member of type vector<vector<vector<int>>> and initialize it with another vector<vector<vector<int>>>. How would it be possible not to have said data twice in memory? Thats more a matter of logic than a matter of c++.
Alternatives
You could store a pointer vector<vector<vector<int>>>* or a reference vector<vector<vector<int>>>& to the vector in an appropriate class member. Or use one of the smart pointers to do so. In any of these cases some serious thinking about memory management is a good idea.
Or you use a move constructor, which is moving the passed in vector in your member vector.
using vec = std::vector<std::vector<std::vector<int>>>;
class foo {
public:
foo() = delete;
foo(const vec&) = delete;
foo(vec&& myvector_in) : myvector(std::move(myvector_in)) {};
private:
vec myvector;
};
Of course that will render that argument passed to the constructor useless but that a trivial consequence of the not-copying you want.
You can pass your vector to that constructor if you first cast it to an rvalue using std::move:
foo my_foo(std::move(test));
The easy way of addressing this issue in C++11 (and newer) is to accept the constructor argument by value:
struct foo {
using vec=std::vector<std::vector<std::vector<int>>>; // from DrSvanHay
foo(vec v) : myvector(std::move(v)) {}
private:
vec myvector;
};
Surprisingly, this actually minimizes copies:
If the client has a vector cv;, cv gets copied into the parameter v, but that copy was necessary to have cv and foo::myvector upon completion.
If the client passes std::move(cv), cv gets moved into v and there is no copy.
If the client passes make_vector(...), the parameter v is move-initialized from the return value (or, in C++17, is the return value).
(In all these cases, v is then moved into foo::myvector, of course.)

How to Create a Constant Reference to an Object?

I have a design which includes 3 classes as my data structures. Let's call the classes A, B, and C.
I have to instantiate objects of A, B, and C while parsing my input. Each object of class C needs to keep track of the corresponding objects in classes A and B. My current implementation looks like this:
class C {
private:
A* a;
B* b;
public:
void setA(A* a);
void setB(B* b);
}
I assign a and b when I create the objects of A and B using new operator. The problem is that I do not want an object of class C to be able to modify a and b. In fact, I only need to know which a and b correspond to a specific c.
One solution could be to define a and b as const pointers/object references. However, in the case of object references, I need to define them as soon as I declare them. The problem is that I am parsing several files and I cannot assign the proper references to a and b when I define them. In other words, a and b are created at different times in my program and may not be available when I create c.
Recently, I read that it's better to avoid raw pointers as much as possible and that's why I'm trying to implement this using object references instead of pointers. Actually, this is giving me a hard time to figure out which one to use at different stages of my program.
My questions are as follows:
1) Is it better to use object references instead of pointers in this specific question?
2) While this approach works for pointers, what should I do to assign constant references to a and b if I prefer to use object references instead of pointers?
3) If I use unique_ptr for a and b, what would be the solution to this problem?
It is good modern C++ style to avoid using raw pointers. See
Chapter 4 of Effective Modern C++ by Scott Meyers for details.
However, this doesn't mean you cannot use pointers. I'd also recommend the reading Effective C++, More Effective C++, and Effective STL.
So nomenclature wise, the reference or pointer are both pointing
to the same object. Under the covers, you are copying around memory
addresses of the object in question.
Now to use a const pointer (or reference) the object itself doesn't
have to be const. If the function takes a const type* or const
type&, the type system will tack on the const for you.
What I think you want is a const pointer. Here the item being
pointed to is const, and not the memory address, so you do not have
to initialize the parameters on construction. Issue with the const
reference is what would you initialize it as? The const type* can
be set to nullptr on construction, but a reference doesn't have a
way to have a null value. I'd say this is important here because
you will want to be able t tell if the caller assigned a value to a
or b. If they didn't, and you properly initialized the member
variables to nullptr, you can test against that to know they weren't
set.
So in this case, the actual memory to be owned by a
std::unique_ptr<> and you would pass down a reference or pointer to
the object. In your situation, sending down the address/pointer is
the only reasonable thing to do. you'll want to initialize the
value to nullptr on construction. Then you'll be able to tell if
the caller assigned a value, because the pointer is not null. If
you wanted to use a const type&, you'd have to set a valid value at
construction time, and since their is no null&, their is no obvious
way to do it.
Also, though this wasn't part of your question, I would document who really owns the memory. One thing I like about using std::unique_ptr<> is that is makes ownership obvious.
Your updated example:
class C {
private:
const A* a;
const B* b;
public:
void setA(const A* a);
void setB(const B* b);
}
The problem is that I do not want an object of class C to be able to modify a and b. In fact, I only need to know which a and b correspond to a specific c.
Use a const pointer:
A const* a;
A const* b;
Then deference them, and they will be objects.
Use pointers instead of references, as if you have an instance of C in either class A or B, you will get an error. Using pointers avoids that
(1)It does not matter; you should use pointers because it is easier to see when you are passing a reference. For example:
function(a); // Not sure if it is a reference
function(&a); // You know it is that actual object
(2) Deference the const pointer, and it will make it a const "reference"

Proper initialisation of smart pointers array

For such case:
class A
{
//implementation
};
class B
{
public:
B();
~B();
private:
std::vector<std::shared_ptr<A>> _innerArray;
};
what should I do in the B() to create an object with valid state? Do I need to manually call default constructor for every A object in array? And do I need to do something special in ~B()? If B class is example of bad design, feel free to say how to make it better. Thanks.
Edit
So, here is a scheme of what I really need here.
So real values stored only in array of A and all other objects are for storing connections.
The easiest example - A = dot, B = Line (or curve) going via selected dots and C = a plane described by lines. Hope it makes question more exact.
To create a B object in a valid state you do not have to do anything more. You even do not have to declare and implement constructor and destructor for B. std::vector<std::shared_ptr<A>> that is a member of B will be default initialized in B's constructor which means it will not have any elements in a container yet. It will also be properly deleted in ~B thanks to std::vector and std::shared_ptr destructors.
On the other hand if you for example want to initialize it somehow (i.e. 3 values) you can use std::vector's std::initializer_list constructor in a B's constructor initialization list. For example:
class B
{
public:
B(): _innerArray{ std::make_shared<A>(),
std::make_shared<A>(),
std::make_shared<A>() } {}
~B() {}
private:
std::vector<std::shared_ptr<A>> _innerArray;
};
Remember that std::make_shared uses perfect forwarding so you pass A's constructor arguments as the function arguments and not the class object itself.
Answering your concerns about the design I would like to encourage you to first think about the exclusive ownership of members in a vector before you decide to share them.
class B
{
public:
B();
~B();
private:
std::vector<std::unique_ptr<A>> _innerArray;
};
Above implementation is more effective on many grounds. First of all it makes your design more clear on who is responsible for the lifetime of As. Next std::unique_ptr is faster because it does not demand thread safe reference counting. And last but not least it does not cost any additional memory (compared to regular C pointer) while std::shared_ptr may take tens of bytes (24-48) to store shared state data which is highly ineffective when you operate on small classes. That is why I always use std::unique_ptr as my first resort smart pointer and I only fallback to std::shared_ptr when it is really needed.
EDIT:
Answering your edit I would create 3 containers of classes A, B, C. Depending of the fact if you need them to be polymorphic or not I would store either values like that (non-polymorphic types):
std::deque<A> as;
std::deque<B> bs;
std::deque<C> cs;
or (polymorphic types):
std::vector<std::unique_ptr<A>> as;
std::vector<std::unique_ptr<B>> bs;
std::vector<std::unique_ptr<C>> cs;
in that order (as must live longer than bs and bs must live longer than cs). Then I would just have std::vector<A*> inside B class and std::vector<B*> inside C class without any smart pointers usage.
I hope that helps.
EDIT:
Changed std::vector to std::deque in the first case which allows references/pointers to container elements survive containers extensions with push_back(). However they will not survive erasing elements, sorting or other stuff.
If you do it like that, the vector has a size of zero elements, i.e. the contents are trivially properly initialized. If the vector were of positive size (e.g. after calling resize on the vector), each of the elements would be properly initialized. Since the elements are shared_ptrs, the default constructor of shared_ptr would be called, which means that you would end up with a vector of empty pointers.
If you want to copy the contents from another container, use the iterator version of the vector constructor:
B (SomeContainerTypeContainingSharedPointers container)
: _innerArray (container.begin (), container.end () ) {
}
If you do not want to initialize the vector from a container, but from somewhere else (e.g. create the objects on the fly) -- write an input iterator type yourself (i.e. kind of a "factory iterator").
The vector is empty so you don't have to do anything special in the default constructor. And you don't need to do anything in B() either. The reference count of the shared_ptrs will be decreased automatically when the vector's destructor is called.
Bt default std::shared_ptr<A> will populate inner ptr with NULL. To create smart pointer use std::make_shared:
_innerArray.push_back(std::make_shared<A>(/*constructor params here*/));
But in your example vector is empty.
The default constructor already does everything needed. You can even leave B() out without any loss.

Trying to store an object in an array but then how to call that object's methods?

I'm not a very experienced c++ coder and this has me stumped. I am passing a object (created elsewhere) to a function, I want to be able to store that object in some array and then run through the array to call a function on that object. Here is some pseudo code:
void AddObject(T& object) {
object.action(); // this works
T* objectList = NULL;
// T gets allocated (not shown here) ...
T[0] = object;
T[0].action(); // this doesn't work
}
I know the object is passing correctly, because the first call to object.action() does what it should. But when I store object in the array, then try to invoke action() it causes a big crash.
Likely my problem is that I simply tinkered with the .'s and *'s until it compiled, T[0].action() compliles but crashes at runtime.
The simplest answer to your question is that you must declare your container correctly and you must define an appropriate assigment operator for your class. Working as closely as possible from your example:
typedef class MyActionableClass T;
T* getGlobalPointer();
void AddInstance(T const& objInstance)
{
T* arrayFromElsewhere = getGlobalPointer();
//ok, now at this point we have a reference to an object instance
//and a pointer which we assume is at the base of an array of T **objects**
//whose first element we don't mind losing
//**copy** the instance we've received
arrayFromElsewhere[0] = objInstance;
//now invoke the action() method on our **copy**
arrayFromElsewhere[0].action();
}
Note the signature change to const reference which emphasizes that we are going to copy the original object and not change it in any way.
Also note carefully that arrayFromElsewhere[0].action() is NOT the same as objInstance.action() because you have made a copy — action() is being invoked in a different context, no matter how similar.
While it is obvious you have condensed, the condensation makes the reason for doing this much less obvious — specifying, for instance, that you want to maintain an array of callback objects would make a better case for “needing” this capability. It is also a poor choice to use “T” like you did because this tends to imply template usage to most experienced C++ programmers.
The thing that is most likely causing your “unexplained” crash is that assignment operator; if you don't define one the compiler will automatically generate one that works as a bitwise copy — almost certainly not what you want if your class is anything other than a collection of simple data types (POD).
For this to work properly on a class of any complexity you will likely need to define a deep copy or use reference counting; in C++ it is almost always a poor choice to let the compiler create any of ctor, dtor, or assignment for you.
And, of course, it would be a good idea to use standard containers rather than the simple array mechanism you implied by your example. In that case you should probably also define a default ctor, a virtual dtor, and a copy ctor because of the assumptions made by containers and algorithms.
If, in fact, you do not want to create a copy of your object but want, instead, to invoke action() on the original object but from within an array, then you will need an array of pointers instead. Again working closely to your original example:
typedef class MyActionableClass T;
T** getGlobalPointer();
void AddInstance(T& objInstance)
{
T** arrayFromElsewhere = getGlobalPointer();
//ok, now at this point we have a reference to an object instance
//and a pointer which we assume is at the base of an array of T **pointers**
//whose first element we don't mind losing
//**reference** the instance we've received by saving its address
arrayFromElsewhere[0] = &objInstance;
//now invoke the action() method on **the original instance**
arrayFromElsewhere[0]->action();
}
Note closely that arrayFromElsewhere is now an array of pointers to objects instead of an array of actual objects.
Note that I dropped the const modifier in this case because I don’t know if action() is a const method — with a name like that I am assuming not…
Note carefully the ampersand (address-of) operator being used in the assignment.
Note also the new syntax for invoking the action() method by using the pointer-to operator.
Finally be advised that using standard containers of pointers is fraught with memory-leak peril, but typically not nearly as dangerous as using naked arrays :-/
I'm surprised it compiles. You declare an array, objectList of 8 pointers to T. Then you assign T[0] = object;. That's not what you want, what you want is one of
T objectList[8];
objectList[0] = object;
objectList[0].action();
or
T *objectList[8];
objectList[0] = &object;
objectList[0]->action();
Now I'm waiting for a C++ expert to explain why your code compiled, I'm really curious.
You can put the object either into a dynamic or a static array:
#include <vector> // dynamic
#include <array> // static
void AddObject(T const & t)
{
std::array<T, 12> arr;
std::vector<T> v;
arr[0] = t;
v.push_back(t);
arr[0].action();
v[0].action();
}
This doesn't really make a lot of sense, though; you would usually have defined your array somewhere else, outside the function.