I have created a class that contains the size and an array of type
Rectangle **a. is the initialization below correct:
C(int size = 1, Rectangle **a = new Rectangle *[1]);
For the copy constructor I've tried this which (edit: don't know how to complete to copy each pointer of the array into the copy, since each element is also a
pointer):
C ( const C & other) : size{other.size}, a{size ? new Rectangle[size] : nullptr}
{
// ....
}
Let the standard library do the work for you. Using std::vector<Rectangle> will be safer, simpler and more reliable.
To answer your question, no your copy constructor is not correct as it only creates a new array of the same size and doesn't copy the existing elements into it.
To avoid permanent brain damage, use std::vector<Rectangle> as a member.
class C {
public:
C(const std::vector<Rectangle> &rectangles) : m_rectangles(rectangles) {}
C(const C &other) : m_rectangles(other.m_rectangles) {}
private:
std::vector<Rectangle> m_rectangles;
};
You need to respect the rule of 0/3/5.
The rule of zero states:
Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle). Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.
RAII containers are a good tool to avoid managing resources. This is the best scenario for you:
#include <vector>
struct Rectangle {};
class C
{
std::vector<Rectangle> _v; // no memory management from C
public:
size_t size() const { return _v.size(); }
Rectangle & operator()(size_t index) { return _v[index]; }
Rectangle const& operator()(size_t index) const { return _v[index]; }
};
If for some reason you need to manage your resources manually, the rules of 3 and 5 kick in.
In C++98, the rule of three states:
If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.
In C++11 and later, the rule of five replaces it:
Because the presence of a user-defined destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions
struct Rectangle {};
struct C
{
size_t _size;
Rectangle* _data;
C() : _size(0), _data(nullptr) {}
C(size_t size) : _size(size), _data(_size == 0 ? nullptr : new Rectangle[_size]) {}
C(C const& other) : _size(other._size), _data(_size == 0 ? nullptr : new Rectangle[_size])
{
std::copy(_data, other._data, other._data + _size);
}
C& operator=(C const& other)
{
C self = other; // copy construction
using std::swap;
swap(*this, self);
return *this;
}
// if necessary: move constructor and move assignment operator
~C() { delete _data; }
};
Related
I have a class B with a set of constructors and an assignment operator.
Here it is:
class B
{
public:
B();
B(const string& s);
B(const B& b) { (*this) = b; }
B& operator=(const B & b);
private:
virtual void foo();
// and other private member variables and functions
};
I want to create an inheriting class D that will just override the function foo(), and no other change is required.
But, I want D to have the same set of constructors, including copy constructor and assignment operator as B:
D(const D& d) { (*this) = d; }
D& operator=(const D& d);
Do I have to rewrite all of them in D, or is there a way to use B's constructors and operator? I would especially want to avoid rewriting the assignment operator because it has to access all of B's private member variables.
You can explicitly call constructors and assignment operators:
class Base {
//...
public:
Base(const Base&) { /*...*/ }
Base& operator=(const Base&) { /*...*/ }
};
class Derived : public Base
{
int additional_;
public:
Derived(const Derived& d)
: Base(d) // dispatch to base copy constructor
, additional_(d.additional_)
{
}
Derived& operator=(const Derived& d)
{
Base::operator=(d);
additional_ = d.additional_;
return *this;
}
};
The interesting thing is that this works even if you didn't explicitly define these functions (it then uses the compiler generated functions).
class ImplicitBase {
int value_;
// No operator=() defined
};
class Derived : public ImplicitBase {
const char* name_;
public:
Derived& operator=(const Derived& d)
{
ImplicitBase::operator=(d); // Call compiler generated operator=
name_ = strdup(d.name_);
return *this;
}
};
Short Answer: Yes you will need to repeat the work in D
Long answer:
If your derived class 'D' contains no new member variables then the default versions (generated by the compiler should work just fine). The default Copy constructor will call the parent copy constructor and the default assignment operator will call the parent assignment operator.
But if your class 'D' contains resources then you will need to do some work.
I find your copy constructor a bit strange:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Normally copy constructors chain so that they are copy constructed from the base up. Here because you are calling the assignment operator the copy constructor must call the default constructor to default initialize the object from the bottom up first. Then you go down again using the assignment operator. This seems rather inefficient.
Now if you do an assignment you are copying from the bottom up (or top down) but it seems hard for you to do that and provide a strong exception guarantee. If at any point a resource fails to copy and you throw an exception the object will be in an indeterminate state (which is a bad thing).
Normally I have seen it done the other way around.
The assignment operator is defined in terms of the copy constructor and swap. This is because it makes it easier to provide the strong exception guarantee. I don't think you will be able to provide the strong guarantee by doing it this way around (I could be wrong).
class X
{
// If your class has no resources then use the default version.
// Dynamically allocated memory is a resource.
// If any members have a constructor that throws then you will need to
// write your owen version of these to make it exception safe.
X(X const& copy)
// Do most of the work here in the initializer list
{ /* Do some Work Here */}
X& operator=(X const& copy)
{
X tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(X& s) throws()
{
/* Swap all members */
}
};
Even if you derive a class D from from X this does not affect this pattern.
Admittedly you need to repeat a bit of the work by making explicit calls into the base class, but this is relatively trivial.
class D: public X
{
// Note:
// If D contains no members and only a new version of foo()
// Then the default version of these will work fine.
D(D const& copy)
:X(copy) // Chain X's copy constructor
// Do most of D's work here in the initializer list
{ /* More here */}
D& operator=(D const& copy)
{
D tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(D& s) throws()
{
X::swap(s); // swap the base class members
/* Swap all D members */
}
};
You most likely have a flaw in your design (hint: slicing, entity semantics vs value semantics). Having a full copy/value semantics on an object from a polymorphic hierarchy is often not a need at all. If you want to provide it just in case one may need it later, it means you'll never need it. Make the base class non copyable instead (by inheriting from boost::noncopyable for instance), and that's all.
The only correct solutions when such need really appears are the envelop-letter idiom, or the little framework from the article on Regular Objects by Sean Parent and Alexander Stepanov IIRC. All the other solutions will give you trouble with slicing, and/or the LSP.
On the subject, see also C++CoreReference C.67: C.67: A base class should suppress copying, and provide a virtual clone instead if "copying" is desired.
You will have to redefine all constructors that are not default or copy constructors. You do not need to redefine the copy constructor nor assignment operator as those provided by the compiler (according to the standard) will call all the base's versions:
struct base
{
base() { std::cout << "base()" << std::endl; }
base( base const & ) { std::cout << "base(base const &)" << std::endl; }
base& operator=( base const & ) { std::cout << "base::=" << std::endl; }
};
struct derived : public base
{
// compiler will generate:
// derived() : base() {}
// derived( derived const & d ) : base( d ) {}
// derived& operator=( derived const & rhs ) {
// base::operator=( rhs );
// return *this;
// }
};
int main()
{
derived d1; // will printout base()
derived d2 = d1; // will printout base(base const &)
d2 = d1; // will printout base::=
}
Note that, as sbi noted, if you define any constructor the compiler will not generate the default constructor for you and that includes the copy constructor.
The original code is wrong:
class B
{
public:
B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment
B& operator= (const B& b); // copy assignment
private:
// private member variables and functions
};
In general, you can not define the copy constructor in terms of the copy assignment, because the copy assignment must release the resources and the copy constructor don't !!!
To understand this, consider:
class B
{
public:
B(Other& ot) : ot_p(new Other(ot)) {}
B(const B& b) {ot_p = new Other(*b.ot_p);}
B& operator= (const B& b);
private:
Other* ot_p;
};
To avoid memory leak , the copy assignment first MUST delete the memory pointed by ot_p:
B::B& operator= (const B& b)
{
delete(ot_p); // <-- This line is the difference between copy constructor and assignment.
ot_p = new Other(*b.ot_p);
}
void f(Other& ot, B& b)
{
B b1(ot); // Here b1 is constructed requesting memory with new
b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!!
}
So, copy constructor and copy assignment are different because the former construct and object into an initialized memory and, the later, MUST first release the existing memory before constructing the new object.
If you do what is originally suggested in this article:
B(const B& b){(*this) = b;} // copy constructor
you will be deleting an unexisting memory.
The code below gives the error:
use of deleted function ‘constexpr B::B(const B&)’
now, I know this happens because the copy constructor is (intentionally) implicitly deleted by specifying a move constructor, and that copying the vector causes the calls to the (deleted) copy constructor. I think I also understand why the vector's copy constructor and assignment operator are used. I clearly want to use the move constructor and assignment operator though: move the object, so also move the vector it contains. So, how do I get my move constructor/assignment operator to use the vector's move constructor/assignment operator?
Here is the code:
#include <vector>
class B {
private:
/* something I don't want to copy */
public:
B() {};
B(B&& orig) {/* move contents */};
B& operator=(B&& rhs) {
/* move contents */
return *this;
};
};
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(orig.vec) {};
A& operator=(A&& rhs) {
vec = rhs.vec;
return *this;
};
};
Just call std::move on the vectors in the expression in which you want to move from them:
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(std::move(orig.vec)) {};
// ^^^^^^^^^
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
// ^^^^^^^^^
return *this;
};
};
Even though you take in rvalue references, rhs and orig are still lvalues in the function, so you need to call std::move on them.
To ensure that the "move" constructor and assignment operators are called, you need to provide an object of the correct value category. The value category is used to determine which operators and constructors could be used.
Use std::move to change the value category (from the lvalue in this case) to a xvalue (an rvalue, that can be moved from).
// ...
A(A&& orig) : vec(std::move(orig.vec)) {};
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
return *this;
};
The move does not copy or change the object in any way, it is simply a cast to an rvalue reference of the argument type - hence modifying the value category.
If all the members of your class are the objects of classes with correctly defined move constructor/assignment operator you should better use default move constructor/assignment operator for your class. This would be much easier and less error-prone and ensure the call of move constructor/assignment operator of class members.
In your particular example it would be:
class A
{
private:
vector<B> vec;
public:
A() : vec() {};
A(A&&) = default;
A& operator=(A&&) = default;
};
I am looking for a good example of how to implement a derived and a base class with move semantics. The more I have thought about it, the more it seems that the default move constructor and assignment move operator will normally do the job because most standard (STL) types and smart pointers are default moveable.
Anyway, in the case we have a class hierarchy, which requires an explicit move implementation, how should I do it - at least as a first cut?
In this example, I am using a raw pointer which I would normally wrap in a std::unique_ptr but I needed an example of something to move which is not default moveable.
Any help would be much appreciated. :)
Currently, I have made the following attempt:
struct BlobA
{
char data[0xaa];
};
struct BlobB
{
char data[0xbb];
};
//----------------------------------------
class Base
{
public:
//Default construct the Base class
//C++11 allows the data members to initialised, where declared. Otherwise you would do it here.
Base()
{
}
//Define the destructor as virtual to ensure that the derived destructor gets called. (In case it is necessary. It's not in this example but still good practice.)
virtual ~Base()
{
delete m_moveableDataInBase; //this is a contrived example to show non-default moveable data, in practice RAII should be used rather than deletes like this
}
//Copy constructor, needs to accept a const ref to another Base class with which it copies the data members
Base(const Base& other) :
m_moveableDataInBase(new BlobA(*other.m_moveableDataInBase)), // copy the other's moveable data
m_pNonMoveableDataInBase(other.m_pNonMoveableDataInBase) // copy the other's non-moveable data
{
}
//Assignment operator uses the canonical copy then swap idiom. It returns a reference to allow chaining: a = b = c
//This is thus implemented in terms of the copy constructor.
Base& operator=(const Base& rhs)
{
Base temp(rhs);
Swap(temp);
return *this;
}
//The move construtor is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and moves the Base part.
Base(Base&& other) noexcept :
m_moveableDataInBase(nullptr) // don't bother allocating our own resources to moveable data because we are about to move (steal) the other's resource
{
Swap(other);
}
//The move assignment operator is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and moves (steals) the data resources from the rhs using swap and copies the non moveable data from the rhs
Base& operator=(Base&& rhs) noexcept
{
//move (steal) the moveable contents from rhs
std::swap(m_moveableDataInBase, rhs.m_moveableDataInBase);
//copy the non-moveable contents from rhs
m_pNonMoveableDataInBase = rhs.m_pNonMoveableDataInBase;
return *this;
}
private:
//this private member swaps the data members' contents.
//It is private because it isn't virtual and only swaps the base contents and is thus not safe as a public interface
void Swap(Base& other)
{
std::swap(m_moveableDataInBase, other.m_moveableDataInBase);
std::swap(m_pNonMoveableDataInBase, other.m_pNonMoveableDataInBase);
}
//an example of some large blob of data which we would like to move instead of copy for performance reasons.
//normally, I would have used a unique_ptr but this is default moveable and I need an example of something that isn't
BlobA* m_moveableDataInBase{ new BlobA };
//an example of some data that we can't or don't want to move
int m_pNonMoveableDataInBase = 123;
};
//----------------------------------------
class Derived : public Base
{
public:
//Default construct the Derived class, this is called after the base class constructor
//C++11 allows the data members to initialised, where declared. Otherwise you would do it here.
Derived()
{
}
//Default virtual destructor, to clean up stuff that can't be done automatically through RAII
virtual ~Derived()
{
delete m_pMoveableDataInDerived; //this is a contrived example to show non-default moveable data, in practice RAII should be used rather than deletes like this
}
//Copy constructor, needs to accept a const ref to another derived class with which it
//first copy constructs the base and then copies the derived data members
Derived(const Derived& other) :
Base(other), // forward to the base copy constructor
m_pMoveableDataInDerived(new BlobB(*other.m_pMoveableDataInDerived)), // copy the other's moveable data
m_pNonMoveableDataInDerived(other.m_pNonMoveableDataInDerived) // copy the other's non-moveable data
{
}
//Assignment operator uses the canonical copy then swap idiom. It returns a reference to allow chaining: a = b = c
//Because it uses the derived copy constructor, which in turn copy constructs the base, we don't forward to the base assignment operator.
Derived& operator=(const Derived& rhs)
{
Derived temp(rhs);
Swap(temp);
return *this;
}
//The move construtor is declared as throwing no eceptions so that it will be called by STL algorithms
//It accepts an rvalue and first moves the Base part and then the Derived part.
//There is no point in allocating any resource before moving so in this example, m_pBlobB is set to nullptr
Derived(Derived&& other) noexcept
: Base(std::move(other)), // forward to base move constructor
m_pMoveableDataInDerived(nullptr) // don't bother allocating our own resources to moveable data because we are about to move (steal) the other's resource
{
Swap(other);
}
//The move assignment operator is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and first calls the base assignment operator and then moves the data resources from the rhs using swap
Derived& operator=(Derived&& rhs) noexcept
{
//forward to the base move operator=
Base::operator=(std::move(rhs));
//move (steal) the moveable contents from rhs
std::swap(m_pMoveableDataInDerived, rhs.m_pMoveableDataInDerived);
//copy the non-moveable contents from rhs
m_pNonMoveableDataInDerived = rhs.m_pNonMoveableDataInDerived;
}
private:
//this member swaps the Derived data members contents.
//It is private because it doesn't swap the base contents and is thus not safe as a public interface
void Swap(Derived& other) noexcept
{
std::swap(m_pMoveableDataInDerived, other.m_pMoveableDataInDerived);
std::swap(m_pNonMoveableDataInDerived, other.m_pNonMoveableDataInDerived);
}
//an example of some large blob of data which we would like to move instead of copy for performance reasons.
//normally, I would have used a unique_ptr but this is default moveable and I need an example of something that isn't
BlobB* m_pMoveableDataInDerived{ new BlobB };
//an example of some data that we can't or don't want to move
int m_pNonMoveableDataInDerived = 456;
};
You have to start out knowing what your class invariants are. A class invariant is something or some relationship that is always true among your data members. And then you want to make sure that your special members can operate with any value which satisfies your class invariants. Special members should not have preconditions (except that all class invariants must be true).
Let's take your example as an example case to discuss. First lets just concentrate on Base. I like to put my private data members up front so that they are close to the special members. This way I can more easily see what defaulted or implicitly declared special members actually do.
Base
class Base
{
//an example of some large blob of data which we would like to move
// instead of copy for performance reasons.
//normally, I would have used a unique_ptr but this is default moveable
// and I need an example of something that isn't
BlobA* m_moveableDataInBase{ new BlobA };
//an example of some data that we can't or don't want to move
int m_pNonMoveableDataInBase = 123;
So far so good, but there is a slight ambiguity here: Can m_moveableDataInBase == nullptr? There is no one right or wrong answer. This is a question that the author of Base must answer, and then write code to enforce.
Also, outline your member functions. Even if you decide you want to inline them, do so outside the declaration. Otherwise your class declaration becomes difficult to read:
class Base
{
BlobA* m_moveableDataInBase{ new BlobA };
int m_pNonMoveableDataInBase = 123;
public:
virtual ~Base();
Base() = default;
Base(const Base& other);
Base& operator=(const Base& rhs);
Base(Base&& other) noexcept;
Base& operator=(Base&& rhs) noexcept;
};
The destructor is the most telling special member. I like to get it declared/defined first:
Base::~Base()
{
delete m_moveableDataInBase;
}
This looks good. But this does not yet answer the question as to whether m_moveableDataInBase can be nullptr. Next, if it exists, the default constructor. Prefer = default definitions when practical.
Now the copy constructor:
Base::Base(const Base& other)
: m_moveableDataInBase(new BlobA(*other.m_moveableDataInBase))
, m_pNonMoveableDataInBase(other.m_pNonMoveableDataInBase)
{
}
Ok, this says something significant:
other.m_moveableDataInBase != nullptr // ever
I glanced ahead and looked at your move constructor, and that leaves the moved-from value with m_moveableDataInBase == nullptr. So we have a problem:
Either there is a bug in your copy constructor, and you should check for the case other.m_moveableDataInBase == nullptr, or
There is a bug in your move constructor and it should not leave the moved-from state with m_moveableDataInBase == nullptr.
Neither solution is the correct one. The Base author has to make this design decision. If he choses 2, there really is no reasonable way to implement the move constructor such that it is any faster than the copy constructor. In that case the thing to do is not write a move constructor and just let the copy constructor do the job. So I'll choose 1 so that there is still a move constructor to talk about. Corrected copy constructor:
Base::Base(const Base& other)
: m_moveableDataInBase(other.m_moveableDataInBase ?
new BlobA(*other.m_moveableDataInBase) :
nullptr)
, m_pNonMoveableDataInBase(other.m_pNonMoveableDataInBase)
{
}
Also, since we chose this invariant, it might not be a bad idea to revisit the default constructor and instead say:
BlobA* m_moveableDataInBase = nullptr;
Now we have a noexcept default constructor.
Next comes the copy assignment operator. Do not fall into the trap of selecting the copy/swap idiom by default. Sometimes this idiom is fine. But it is often poorly performing. And performance is more important than code reuse. Consider this alternative to copy/swap:
Base&
Base::operator=(const Base& rhs)
{
if (this != &rhs)
{
if (m_moveableDataInBase == nullptr)
{
if (rhs.m_moveableDataInBase != nullptr)
m_moveableDataInBase = new BlobA(*rhs.m_moveableDataInBase);
}
else // m_moveableDataInBase != nullptr
{
if (rhs.m_moveableDataInBase != nullptr)
*m_moveableDataInBase = *rhs.m_moveableDataInBase;
else
{
delete m_moveableDataInBase;
m_moveableDataInBase = nullptr;
}
}
m_pNonMoveableDataInBase = rhs.m_pNonMoveableDataInBase;
}
return *this;
}
If it is common for values of Base to have m_moveableDataInBase != nullptr, then this rewrite is significantly faster than copy/swap. In this common case, copy/swap always does 1 new and 1 delete. This version does 0 news and 0 deletes. It just copies 170 bytes.
And if we had chosen the design where it is an invariant that m_moveableDataInBase != nullptr, then the copy assignment gets even simpler:
Base&
Base::operator=(const Base& rhs)
{
*m_moveableDataInBase = *rhs.m_moveableDataInBase;
m_pNonMoveableDataInBase = rhs.m_pNonMoveableDataInBase;
return *this;
}
Minimizing calls to the heap is not premature optimization. It is engineering. It is what move semantics is made out of. This is precisely why std::vector and std::string copy assignment do not use the copy/swap idiom. It would be too slow.
Move constructor: I would code it like this:
Base::Base(Base&& other) noexcept
: m_moveableDataInBase(std::move(other.m_moveableDataInBase))
, m_pNonMoveableDataInBase(std::move(other.m_pNonMoveableDataInBase))
{
other.m_moveableDataInBase = nullptr;
}
This saves a few loads and stores. I didn't bother inspecting the generated assembly. I urge you to do so before choosing your implementation over this one. In a noexcept move constructor, count loads and stores.
As a style-guide, I like to move the members even when I know they are scalars and the move has no impact. This saves the reader of the code from having to ensure that all non-moved members are scalars.
Your move assignment looks fine to me:
Base&
Base::operator=(Base&& rhs) noexcept
{
//move (steal) the moveable contents from rhs
std::swap(m_moveableDataInBase, rhs.m_moveableDataInBase);
//copy the non-moveable contents from rhs
m_pNonMoveableDataInBase = rhs.m_pNonMoveableDataInBase;
return *this;
}
The one time when you don't want to do this is when you have non-memory resources on the lhs that need to be destructed immediately, as opposed to swapped to the rhs. But your example is only swapping memory.
Derived
For Derived I would write it exactly as I've shown for Base, except for first copying/moving the Base exactly as you show in your code. For example here is the move constructor:
Derived::Derived(Derived&& other) noexcept
: Base(std::move(other))
, m_pMoveableDataInDerived(std::move(other.m_pMoveableDataInDerived))
, m_pNonMoveableDataInDerived(std::move(other.m_pNonMoveableDataInDerived))
{
other.m_pMoveableDataInDerived = nullptr;
}
Also tag ~Dervied() with override instead of virtual. You want the compiler to tell you if you've accidentally somehow not overridden ~Base() with ~Derived().
class Derived : public Base
{
BlobB* m_pMoveableDataInDerived = nullptr;
int m_pNonMoveableDataInDerived = 456;
public:
~Derived() override;
Derived() = default;
Derived(const Derived& other);
Derived& operator=(const Derived& rhs);
Derived(Derived&& other) noexcept;
Derived& operator=(Derived&& rhs) noexcept;
};
Test
Also test all six special members (whether you have them or not) with static_assert and type-traits:
static_assert(std::is_nothrow_destructible<Base>{}, "");
static_assert(std::is_nothrow_default_constructible<Base>{}, "");
static_assert(std::is_copy_constructible<Base>{}, "");
static_assert(std::is_copy_assignable<Base>{}, "");
static_assert(std::is_nothrow_move_constructible<Base>{}, "");
static_assert(std::is_nothrow_move_assignable<Base>{}, "");
static_assert(std::is_nothrow_destructible<Derived>{}, "");
static_assert(std::is_nothrow_default_constructible<Derived>{}, "");
static_assert(std::is_copy_constructible<Derived>{}, "");
static_assert(std::is_copy_assignable<Derived>{}, "");
static_assert(std::is_nothrow_move_constructible<Derived>{}, "");
static_assert(std::is_nothrow_move_assignable<Derived>{}, "");
You can even test these for your Blob types:
static_assert(std::is_trivially_destructible<BlobA>{}, "");
static_assert(std::is_trivially_default_constructible<BlobA>{}, "");
static_assert(std::is_trivially_copy_constructible<BlobA>{}, "");
static_assert(std::is_trivially_copy_assignable<BlobA>{}, "");
static_assert(std::is_trivially_move_constructible<BlobA>{}, "");
static_assert(std::is_trivially_move_assignable<BlobA>{}, "");
static_assert(std::is_trivially_destructible<BlobB>{}, "");
static_assert(std::is_trivially_default_constructible<BlobB>{}, "");
static_assert(std::is_trivially_copy_constructible<BlobB>{}, "");
static_assert(std::is_trivially_copy_assignable<BlobB>{}, "");
static_assert(std::is_trivially_move_constructible<BlobB>{}, "");
static_assert(std::is_trivially_move_assignable<BlobB>{}, "");
Summary
In summary, give each of the six special members all the loving care they deserve, even if the result is to inhibit them, implicitly declare them, or explicitly default or delete them. The complier-generated move members will move each base, then move each non-static data member. Prefer that recipe, default it when you can, and simply augment it when it is necessary.
Highlight your class API by moving member function definitions out of the class declaration.
Test. At the very least test that you do or do not have all 6 special members, and if you have them, if they are noexcept or trivial (or not).
Use copy/swap with caution. It can be a performance killer.
Anyway, in the case we have a class hierarchy, which requires an
explicit move implementation, how should I do it - at least as a first
cut?
Don't.
You don't move the base class. You move the pointer to the base class. For the derived class, you can move it but then you know what the derived class is so you can just write the move constructor/assignment operator accordingly.
Also, raw pointers are completely movable. How do you think unique_ptr is implemented?
I would ditch the "swap with other" approach which doesn't help with the readablity and go with simple assignments instead.
class A{
int * dataA;
public:
A() { dataA = new int(); }
virtual ~A() {delete dataA; }
A(A&& rhs) noexcept { dataA = rhs.dataA; rhs.dataA = nullptr; }
A& operator = (A&& rhs) noexcept {
if (this != &rhs){
if (dataA) delete dataA;
dataA = rhs.dataA;
rhs.dataA = nullptr;
}
return *this;
}
}
class B: public A{
int* dataB;
public:
B(){ dataB = new int(); }
virtual ~B() {delete dataB; }
B(B&& rhs) noexcept : A(std::move(rhs)) { dataB = rhs.dataB; rhs.dataB = nullptr; }
B& operator = (B&& rhs) noexcept {
A::operator == (std::move(rhs));
if (this != &rhs){
if (dataB) delete dataB;
dataB = rhs.dataB;
rhs.dataB = nullptr;
}
return *this;
}
}
call the parent move constructor to move the parent-part of the object. do the rest in the move constructor.
the same goes for the assignment operator.
Is this a valid way to create an assignment operator with members that are references?
#include <new>
struct A
{
int &ref;
A(int &Ref) : ref(Ref) { }
A(const A &second) : ref(second.ref) { }
A &operator =(const A &second)
{
if(this == &second)
return *this;
this->~A();
new(this) A(second);
return *this;
}
}
It seems to compile and run fine, but with c++ tendency to surface undefined behavior when least expected, and all the people that say its impossible, I think there is some gotcha I missed. Did I miss anything?
It's syntactically correct. If the placement new throws, however, you
end up with an object you can't destruct. Not to mention the disaster
if someone derives from your class. Just don't do it.
The solution is simple: if the class needs to support assignment, don't
use any reference members. I have a lot of classes which take reference
arguments, but store them as pointers, just so the class can support
assignment. Something like:
struct A
{
int* myRef;
A( int& ref ) : myRef( &ref ) {}
// ...
};
Another solution is to use the reference_wrapper class ( in functional header ) :
struct A
{
A(int& a) : a_(a) {}
A(const A& a) : a_(a.a_) {}
A& operator=(const A& a)
{
a_ = a.a_;
return *this;
}
void inc() const
{
++a_;
}
std::reference_wrapper<int>a_;
};
What you do its technically correct as far as I know, but it generates trouble. For instance, consider what happens with a derived class from A, since its assignment operator generates a new object (slicing). Can't you just turn the reference into a pointer within your class?
Besides that, copy constructors and assignment operators usually take its argument by const&.
What you do is correct, but it is not very exception safe way of writing an copy assignment operator. Also, You should consider using a pointer member rather than an reference member.
You should implement it using the Copy and Swap Idiom. It has atleast 3 advantages over your implementation.
I have a class B with a set of constructors and an assignment operator.
Here it is:
class B
{
public:
B();
B(const string& s);
B(const B& b) { (*this) = b; }
B& operator=(const B & b);
private:
virtual void foo();
// and other private member variables and functions
};
I want to create an inheriting class D that will just override the function foo(), and no other change is required.
But, I want D to have the same set of constructors, including copy constructor and assignment operator as B:
D(const D& d) { (*this) = d; }
D& operator=(const D& d);
Do I have to rewrite all of them in D, or is there a way to use B's constructors and operator? I would especially want to avoid rewriting the assignment operator because it has to access all of B's private member variables.
You can explicitly call constructors and assignment operators:
class Base {
//...
public:
Base(const Base&) { /*...*/ }
Base& operator=(const Base&) { /*...*/ }
};
class Derived : public Base
{
int additional_;
public:
Derived(const Derived& d)
: Base(d) // dispatch to base copy constructor
, additional_(d.additional_)
{
}
Derived& operator=(const Derived& d)
{
Base::operator=(d);
additional_ = d.additional_;
return *this;
}
};
The interesting thing is that this works even if you didn't explicitly define these functions (it then uses the compiler generated functions).
class ImplicitBase {
int value_;
// No operator=() defined
};
class Derived : public ImplicitBase {
const char* name_;
public:
Derived& operator=(const Derived& d)
{
ImplicitBase::operator=(d); // Call compiler generated operator=
name_ = strdup(d.name_);
return *this;
}
};
Short Answer: Yes you will need to repeat the work in D
Long answer:
If your derived class 'D' contains no new member variables then the default versions (generated by the compiler should work just fine). The default Copy constructor will call the parent copy constructor and the default assignment operator will call the parent assignment operator.
But if your class 'D' contains resources then you will need to do some work.
I find your copy constructor a bit strange:
B(const B& b){(*this) = b;}
D(const D& d){(*this) = d;}
Normally copy constructors chain so that they are copy constructed from the base up. Here because you are calling the assignment operator the copy constructor must call the default constructor to default initialize the object from the bottom up first. Then you go down again using the assignment operator. This seems rather inefficient.
Now if you do an assignment you are copying from the bottom up (or top down) but it seems hard for you to do that and provide a strong exception guarantee. If at any point a resource fails to copy and you throw an exception the object will be in an indeterminate state (which is a bad thing).
Normally I have seen it done the other way around.
The assignment operator is defined in terms of the copy constructor and swap. This is because it makes it easier to provide the strong exception guarantee. I don't think you will be able to provide the strong guarantee by doing it this way around (I could be wrong).
class X
{
// If your class has no resources then use the default version.
// Dynamically allocated memory is a resource.
// If any members have a constructor that throws then you will need to
// write your owen version of these to make it exception safe.
X(X const& copy)
// Do most of the work here in the initializer list
{ /* Do some Work Here */}
X& operator=(X const& copy)
{
X tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(X& s) throws()
{
/* Swap all members */
}
};
Even if you derive a class D from from X this does not affect this pattern.
Admittedly you need to repeat a bit of the work by making explicit calls into the base class, but this is relatively trivial.
class D: public X
{
// Note:
// If D contains no members and only a new version of foo()
// Then the default version of these will work fine.
D(D const& copy)
:X(copy) // Chain X's copy constructor
// Do most of D's work here in the initializer list
{ /* More here */}
D& operator=(D const& copy)
{
D tmp(copy); // All resource all allocation happens here.
// If this fails the copy will throw an exception
// and 'this' object is unaffected by the exception.
swap(tmp);
return *this;
}
// swap is usually trivial to implement
// and you should easily be able to provide the no-throw guarantee.
void swap(D& s) throws()
{
X::swap(s); // swap the base class members
/* Swap all D members */
}
};
You most likely have a flaw in your design (hint: slicing, entity semantics vs value semantics). Having a full copy/value semantics on an object from a polymorphic hierarchy is often not a need at all. If you want to provide it just in case one may need it later, it means you'll never need it. Make the base class non copyable instead (by inheriting from boost::noncopyable for instance), and that's all.
The only correct solutions when such need really appears are the envelop-letter idiom, or the little framework from the article on Regular Objects by Sean Parent and Alexander Stepanov IIRC. All the other solutions will give you trouble with slicing, and/or the LSP.
On the subject, see also C++CoreReference C.67: C.67: A base class should suppress copying, and provide a virtual clone instead if "copying" is desired.
You will have to redefine all constructors that are not default or copy constructors. You do not need to redefine the copy constructor nor assignment operator as those provided by the compiler (according to the standard) will call all the base's versions:
struct base
{
base() { std::cout << "base()" << std::endl; }
base( base const & ) { std::cout << "base(base const &)" << std::endl; }
base& operator=( base const & ) { std::cout << "base::=" << std::endl; }
};
struct derived : public base
{
// compiler will generate:
// derived() : base() {}
// derived( derived const & d ) : base( d ) {}
// derived& operator=( derived const & rhs ) {
// base::operator=( rhs );
// return *this;
// }
};
int main()
{
derived d1; // will printout base()
derived d2 = d1; // will printout base(base const &)
d2 = d1; // will printout base::=
}
Note that, as sbi noted, if you define any constructor the compiler will not generate the default constructor for you and that includes the copy constructor.
The original code is wrong:
class B
{
public:
B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment
B& operator= (const B& b); // copy assignment
private:
// private member variables and functions
};
In general, you can not define the copy constructor in terms of the copy assignment, because the copy assignment must release the resources and the copy constructor don't !!!
To understand this, consider:
class B
{
public:
B(Other& ot) : ot_p(new Other(ot)) {}
B(const B& b) {ot_p = new Other(*b.ot_p);}
B& operator= (const B& b);
private:
Other* ot_p;
};
To avoid memory leak , the copy assignment first MUST delete the memory pointed by ot_p:
B::B& operator= (const B& b)
{
delete(ot_p); // <-- This line is the difference between copy constructor and assignment.
ot_p = new Other(*b.ot_p);
}
void f(Other& ot, B& b)
{
B b1(ot); // Here b1 is constructed requesting memory with new
b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!!
}
So, copy constructor and copy assignment are different because the former construct and object into an initialized memory and, the later, MUST first release the existing memory before constructing the new object.
If you do what is originally suggested in this article:
B(const B& b){(*this) = b;} // copy constructor
you will be deleting an unexisting memory.