One of the purposes of a copy constructor is so that pointers declared in a copy point to their respective members, and not simply back to the originals members, but how exactly is this implemented?
say your constructor is:
foo::foo(int i)
{
blah = i;
bar = new whatever; //bar is a pointer to a whatever
}
so what should the copy constructor implementation look like? is the only thing you have to put in it something like:
bar = this->whatever;
or
bar = whatever;
or should it contain everything the normal constructor does?
It entirely depends on what a foo is, and its relation to a whatever.
Possible options are:
Sharing the object
foo::foo(const foo& other) : blah(other.blah), bar(other.bar)
{ }
Creating another object
foo::foo(const foo& other) : blah(other.blah), bar(new whatever)
{ }
Creating a new copy of the object
foo::foo(const foo& other) : blah(other.blah), bar(new whatever(*other.bar))
{ }
No, copy constructor is about copying members of the object, it doesn't have much to do with pointers, unless the members are. Like if you have an object point with x and y members, you would make a copy constructor
point(const point& p) : x(p.x), y(p.y) { }
and it may have another constructor like
point(int x_,int y_) : x(x_), y(y_) { }
If you have a pointer member, it depends on the object and how you want to handle it — you may want to copy the pointer or create another pointer, etc. Depends on the data it is pointing to.
If you don't have a copy constructor, a default one will be created for you which does a shallow copy, where each member variable of one object is simply assigned to the other, as opposed to a deep copy.
Therefore if you have a pointer to a dynamically allocated buffer, then the default copy constructor is going to simply assign that pointer to the copy's pointer, rather than creating it's own new buffer to point to and copying the contents of the buffer into the new one. eg.
class DynamicIntArray {
private:
int *_array;
size_t _size;
public:
DynamicIntArray(size_t size) : _array(new int[size]), _size(size) { }
DynamicIntArray (const DynamicIntArray &d) // copy constructor
{
delete[] _array;
_array = new int[d._size];
_size = d._size;
std::copy(_array, d._array, d._array + d._size);
}
/* destructor, assignment operator, etc */
};
If you didn't create a default copy constructor, then the default one created would simply assign d._array to _array, which would cause serious problems. ie. Instead of the above, it would do:
_array = d._array;
_size = d._size;
Note that if you have a copy constructor, you should probably have an assignment operator and a destructor (look up the rule-of-three).
Related
I have a long class with a lot of data members. I want to write a copy constructor for it. But, if I write my own copy constructor, I lose access to the default copy constructor.
I just want to repair a few pointers in my own copy constructor. So I want to have a shallow copy of the object which can be done by the default copy constructor.
Is there a possibility to access the default copy constructor when I have my own copy constructor?
Wrap the things you don't want to change in a struct, and derive (privately) from it. In your copy constructor, simply invoke the copy constructor of your base class.
No you cannot have both default and your own copy c-tor.
But there are two workarounds with this problem:
1 Enclose your pointers in some class with defined copy semantics
Example:
class A {
public:
private:
int trivial1;
int trivial2;
...
SomePointer nontrivialMember;
};
class SomePointer {
public:
SomePointer(const SomePointer&); // here the non trivial part of A copy semantics
int* nonTrivialMember;
};
2 Enclose the trivial parameters in some trivial structure
Example:
class A {
public:
A(const A& o) : data(o.data) {
// non trivial part
}
private:
struct Data {
int trivial1;
int trivial2;
...
} data;
int* nontrivialMember;
};
I would always select the first solution.
[UPDATE]
There is also 3rd solution, very similar to my second, enclose your trivial part in privately inherited base class. I'd still prefer the 1st solution.
The simplest approach to this would be to wrap up the pointers into classes that will perform the 'repair' manually in their copy constructor, then you can happily use the default copy constructor.
No, there is no way to call the default copy constructor from an user defined copy constructor.
You can either use the default or your own, not both. If you want to choose different functionality for different objects you should just write a member function that handles that case.
void DeepCopy(MyClass* rhs);
For example.
You cannot access default copy ctor if you created your own - compiler just doesn't generate it. But ther is workaround - split you class into data structure and logic.
See example:
struct Data
{
int i;
std::string s;
Data(): i(), s() {}
};
class Code: private Data
{
public:
Code() {}
Code(const Code& rhs): Data(rhs) // Call default copy ctor
{
i = 42; // Your copy part
return *this;
}
};
My solution is a simple memcpy() instead of the impossible call to the implicit (compiler generated) copy constructor, as the example shown below:
Class Foo
{
public:
...
Foo (Foo & other) {
// copies trivial part (and non-trivial part with possible wrong values)
memcpy(this, &other, sizeof(Foo));
// your non-trivial part here, overwrites the wrong values (if any) above.
}
}
Yet the side-effect is that the memcpy() will also copy those non-trivial part, which is a waste. If the non-trivial part does not contain too much space, I will prefer my solution.
For example, a class like below wastes only 4 byte copy of the one pointer, assuming the size of a pointer is 4 bytes.
Class Bar
{
int x, y, z;
// memcpy() wastes these 4 bytes copy,
// since actual copy constructor wants a new string
string *s;
}
This worked for me... (C++11, don't know if it works on older std)
Not sure why it doesn't end up in an endless loop.
class Foo {
public:
Foo(const Foo &orig) {
*this = orig;
... exchange pointers, do own stuff
}
I have a class that looks somewhat like this:
class S
{
public:
int* data;
S() : data(new int[10]) {}
};
The constructor allocates the memory of 10 integers, and the default copy constructor as expected merely copies the pointer itself rather than the content.
Even if there is an instance of S that has const modifier, I can modify the data that data points to, since that data itself does not have const modifier. I could avoid this by making data private and only allowing write access via a non-const method like so:
class S
{
private:
int* data;
public:
S() : data(new int[10]) {}
int& operator(size_t i)
{
return data[i];
}
const int& operator(size_t i) const
{
return data[i];
}
};
But now I can use the copy constructor to circumvent the constness of the instance of S like so:
void main()
{
const S a; // Allocates memory
S b(a); // Also points to memory allocated for a
b(1) = 3; // Writes to a even though it is not supposed to be mutable
}
What would be an elegant way to solve this problem (potentially using templates)?
The data pointed to by an instance of const S should not be mutable at all using only this instance.
Copy constructor should only copy pointer, but not make a deep copy of the data.
Both a const S and an S should be creatable via a copy constructor given an instance of S such that the const instance cannot modify the data, but the non-const instance can.
It is possible to know in the copy constructor if the object being copied is const by providing two different copy constructors, one which takes a const parameter and one which does not. The compiler will select whichever version matches the passed parameter. Set a flag in the constructor so it can throw an error when a non-const operation is performed.
The best way to avoid the leaked memory shown in the question is to used a smart pointer like std::shared_ptr rather than a raw pointer. Unfortunately shared_ptr is meant for single objects, not arrays; workarounds are possible as in this StackOverflow question. I'm not going to try to solve this now, the code below still has the leak.
To be complete you should follow the Rule of Three and provide an operator= and destructor as well. I left this as an exercise for the reader.
class S
{
private:
int* data;
bool is_const;
public:
S() : data(new int[10]), is_const(false) { data[1] = 42; }
S(const S& other) : data(other.data), is_const(true) {}
S(S& other) : data(other.data), is_const(false) {}
int& operator()(size_t i)
{
if (is_const)
throw std::logic_error("non-const operation attempted");
return data.ptr[i];
}
const int& operator()(size_t i) const
{
return data.ptr[i];
}
};
See it in action: http://ideone.com/SFN89M
Delete the copy constructor (and assignment operator) for S. Create a new proxy class (SCopy) that holds a pointer to an S object (which is passed in to the constructor for SCopy). SCopy would then implement the const int &operator() const and not the non-const version.
This would then allow you to implement a destructor in S that would free the memory you're currently leaking.
struct foo{
int* i;
foo(): i(new int(42)){}
foo(const foo&) = delete;
foo(foo&&) = default;
~foo(){
std::cout << "destructor: i" << std::endl;
delete(i);
}
};
int main()
{
foo f;
auto sp_f = std::make_shared<foo>(std::move(f));
}
This is bad because it seems that the destructor of f will be called once it moves into the shared_ptr. The shared_ptr will have a deleted pointer and will delete it after it goes out of scope, which means the pointer will be deleted two times.
How do I avoid this problem?
You need to define the move constructor to prevent deletion from the moved-from object:
foo(foo&& f): i(f.i) {
f.i = nullptr;
}
Now when to destructor of the old object is run, it won't delete the i, since deleting a null pointer is a no-op.
You should also define a move assignment operator and delete the copy assignment operator too.
The rule of three is really the rule of five now. If you have a class that can be moved from, you should define the move semantics yourself (plus the copy, destructor, etc).
As to how to do that, to quote cppreference's page on std::move, "... objects that have been moved from are placed in a valid but unspecified state." The unspecified state is typically what the object would look like if it had been default initialized, or what would happen if the objects had swap called on them.
A straightforward way is, as #zenith has answered, is to have the move constructor (or assignment operator) set the original pointer to nullptr. This way the data isn't freed, and the original object is still in a valid state.
Another common idiom is, as mentioned, to use swap. If a class needs its own copy and move semantics, a swap method would be handy as well. The move constructor would delegate initialization to the default constructor, and then call the swap method. In a move assignment operator, just call swap. The object being moved into will get the resources, and the other object's destructor will free the original ones.
It would usually look like this:
struct Foo
{
void* resource; //managed resource
Foo() : resource(nullptr) {} //default construct with NULL resource
Foo(Foo&& rhs) : Foo() //set to default value initially
{
this->swap(rhs); //now this has ownership, rhs has NULL
}
~Foo()
{
delete resource;
}
Foo& operator= (Foo&& rhs)
{
this->swap(rhs); //this has ownership, rhs has previous resource
}
void swap(Foo& rhs) //basic swap operation
{
std::swap(resource, rhs.resource); //thanks #M.M
}
};
Consider for example a template container class that holds a buffer that is allocated on the heap:
T *_buffer = new T[SIZE]
Just a simple pointer to c array of type T.
This class is templated. However I am having issues with performing a deep copy of an object into my buffer.
In my unit test, i set up a test class:
class test
{
public:
int* _ptrInt;
test() {_ptrInt = nullptr;}
test(const int i)
{
_ptrInt = new int;
*_ptrInt = i;
}
test(const test& other)
{
_ptrInt = new int;
*_ptrInt = *other._ptrInt;
}
~test()
{
delete _ptrInt;
}
};
on my container I call set, passing a temporary as the data:
container.set(0, test(5));
// destructor called on copy immediately after statement, invalidating deep copy in buffer
void set (const int& index, const T& data)
{
int i = realign(index);
T copy = data;
_buffer[i==SIZE?i-1:i] = copy; // ternary statement and index work
}
however, _buffer takes copy as a reference, the moment copy goes out of scope, it deletes the same pointer that is held in the _buffer. I am trying to force the _buffer to assign by value. But I have had no luck.
memcpy still copies the pointers to point to the same address
test copy constructor is correctly called
move semantics would require class to have move constructor
std::vector somehow implements this to copy correctly, whether its T/T*, heap/stack, with/without move constructor, so I know it must be possible
Is there a way I can assign by value to the _buffer on the heap?
You are "assigning by value." However, your test class doesn't implement the assignment operator operator=, so the assignment invokes the compiler-generated default assignment operator which simply copies member-by-member. Hence the problems with shallow assignment.
Also, your copy constructor will explode if other._ptrInt is nullptr.
Consider the following simplified example of a class holding a shared resource:
class array
{
typedef std::array<float, 1000> resource;
public:
// default constructor, creates resource
array() : p_(std::make_shared<resource>()), a_(0), b_(1000) {}
// create view
array operator()(int a, int b) { return array(p_, a_+a, a_+b); }
// get element
float& operator[](int i) { return (*p_)[a_+i]; }
private:
std::shared_ptr<resource> p_;
int a_, b_;
// constructor for views
array(std::shared_ptr<resource> p, int a, int b) : p_(p), a_(a), b_(b) {}
};
Now I'm wondering how to define a semantics for this class that doesn't confuse its users. For example, I'd like to allow operator=() to copy elements:
array x, y;
x = y; // copies all elements from y's storage to x's
x(30,40) = y(50,60); // copies 10 elements
But then, to be consistent, shouldn't the copy constructor and the copy assignment operator always copy? What about:
array z = x(80,90); // will create an array referencing x's storage
In this case, the copy will be elided by the compiler, so no matter what my copy assignment operator does, z will hold a reference to x's storage. There's no way around this.
So does it make more sense for assignment to always create a reference, and copying to be declared explicitly? For example, I could define a wrapper class copy_wrapper, assignment of which forces copying of elements:
class copy_wrapper
{
array& a_;
public:
explicit copy_wrapper(array& a) : a_(a) {}
array& get() { return a_; }
};
class array
{
// ...
array& operator=(const array& a); // references
array& operator=(copy_wrapper c); // copies
copy_wrapper copy() { return copy_wrapper(*this); }
};
This would force users to write:
array x, y;
x(30,40) = y(50,60).copy(); // ok, copies
x(30,40) = y(50,60); // should this throw a runtime error?
x = y; // surprise! x's resource is destructed.
A bit cumbersome, and worse than that: not what you expect.
How should I deal with this ambiguity?
Does it make more sense for assignment to always create a reference, and copying to be declared explicitly?
In practice, no. Expecting developers to remember that a = b will not copy is what got std::auto_ptr into hot water. The C++11 standards committee also decided on std::move and std::forward to allow developers to explicitly say "I'm not making copies."
What about: array z = x(80,90); In this case, the copy will be elided by the compiler, so no matter what my copy assignment operator does, z will hold a reference to x's storage. There's no way around this.
This was your giveaway. You want "array" and "view to an array" to act differently, but you're representing them both with class array.
Implement a new class arrayview with a similar public interface and move the ranges a_ and b_ to arrayview.
array x, y;
x = y; // "array = array" copies all elements
x(30,40) = y(50,60); // "arrayview = arrayview" copies element range
arrayview v = x(80,90); // "arrayview(const arrayview&) shallow copies
x(30,40) = y; // NOT DEFINED, since this has no clear meaning
array z = x(80,90); // NOT DEFINED, since this has no clear meaning
Note that in your arrayview assignment operator, it should be valid to copy from an arrayview with the same resource, but in that specific case you should check whether std::copy_backward is needed instead of std::copy.