overloading assignment operator - polymorphic containers - c++

i have a base class, 2 derived classes and an entitymanager class that has a container of base pointers pointing to derived objects. i have a virtual clone method in the base to take care of the copy constructors in the derived classes, but im having trouble wrapping my head around overloading the assignment operator and preventing slicing, could someone please help with that and perhaps review how ive handled the entitymanager copy constructor? i think its ok
class System
{
public:
virtual System* clone()=0;
};
class projectile :public System
{
public:
projectile* clone()
{
return new projectile(*this);
}
};
class player : public System
{
public:
player* clone()
{
return new player(*this);
}
};
class EntityManager
{
private:
vector<System*> theEntities;
public:
EntityManager(){}
EntityManager(EntityManager& other)
{
for (size_t i=0;i<other.theEntities.size();i++)
theEntities.push_back(other.theEntities[i]->clone());
}
void init()
{
projectile* aProjectile = new projectile;
player* aPlayer = new player;
theEntities.push_back(aProjectile);
theEntities.push_back(aPlayer);
}
};
int main (int argc, char * const argv[])
{
EntityManager originalManager;
originalManager.init();
EntityManager copyManager(originalManager);
return 0;
}

Add a swap member that swaps the containers, then implement assignment as copy and swap:
void swap(EntityManager& other)
{ theEntities.swap(other.theEntities); }
EntityManager& operator=(EntityManager other)
{ swap(other); return *this; }
The argument to the assignment operator will be copied using the copy constructor you've already written, then you swap the data, so the data that belonged to *this will be destroyed when that parameter goes out of scope, and *this owns the newly copied data.
Re-using the copy constructor in this way means you only need to implement a correct copy constructor (and correct swap, which is usually easy to get right) and your assignment operator is really simple and automatically correct.
N.B. your init member and copy ctor are not exception safe, if any push_back operation throws an exception you leak memory. You're also missing a destructor, but I assume that's present in the real code.

Related

copy elision and virtual cloning

How to avoid unnecessary copying in the following scenario? Class A contains base-type pointer to big object.
class A{
BigBaseClass *ptr;
A(const BigBaseClass& ob);
~A(){delete ptr;}
};
Sometimes I will need object ob to be copied. So I implement virtual cloning:
class BigBaseClass{
virtual BigBaseClass* clone() {return new BigBaseClass(*this);}
};
class BigDerivedClass : BigBaseClass{
virtual BigDerivedClass* clone() {return new BigDerivedClass(*this);}
};
A::A(const BigBaseClass& ob):ptr(ob.clone(){}
But sometimes I will create temporary BigDerivedClass object and use it to construct class A:
A a{BigDerivedClass()};
or
BigDerivedClass f(){
BigDerivedClass b;
/*constructing object*/
return b;
}
A a{f()};
Here there is no need to copy created object and then delete it. It's possible to create this object directly in the heap and store its address in a.ptr.
But it seems unlikely to me that compiler is smart enough to implement copy elision here (or is it?). So what would you suggest to avoid such unnecessary copying?
The compiler will not elide the construction of a copy via clone(): copy elision is only allowed in very specific situations. In all cases where the compiler is allowed to do copy elision the life-times of the objects involved are entirely controlled by the compiler. The four situations are (for details see 12.8 [class.copy] paragraph 8):
Returning a local name by value.
Throwing a local object.
Copying a temporary object which isn't bound to a reference.
When catching by value.
The details when copy-elision is applicable even in these situations are somewhat non-trivial. In any case, return new T(*this); doesn't fit any of these situations.
Typical big objects don't hold their data as part of the object. Instead, they typically hold some data structures which can be moved. If you want to retain the simplicity when using A{f()} without wanting to copy the result of f(), you can get away with a move constructor calling a virtual function transferring the content instead of copying it:
#include <utility>
class BigBaseClass {
public:
virtual ~BigBaseClass() {}
virtual BigBaseClass* clone() const = 0;
virtual BigBaseClass* transfer() && = 0;
};
class A{
BigBaseClass *ptr;
public:
A(BigBaseClass&& obj): ptr(std::move(obj).transfer()) {}
A(BigBaseClass const& obj): ptr(obj.clone()) {}
~A(){delete ptr;}
};
class BigDerivedClass
: public BigBaseClass {
BigDerivedClass(BigDerivedClass const&); // copy the content
BigDerivedClass(BigDerivedClass&&); // transfer the content
BigDerivedClass* clone() const { return new BigDerivedClass(*this); }
BigDerivedClass* transfer() && { return new BigDerivedClass(std::move(*this)); }
};
BigDerivedClass f() {
return BigDerivedClass();
}
int main()
{
A a{f()};
}
Whether move construction does help copying the big objects does depend on how the objects are internally implemented. If they object essentially just contains a couple of pointers to the actual large data, move construction should avoid any relevant cost as transferring the pointers would be negligible compared to setting up the actual data. If the data is actually held within the object the transfer wouldn't really help (although it is generally a bad idea to so anyway for a variety of reasons).
class BigBaseClass
{
public:
virtual ~BigBaseClass() {}
virtual BigBaseClass* clone() const { return new BigBaseClass(*this); }
};
class BigDerivedClass : public BigBaseClass
{
public:
BigDerivedClass* clone() const override { return new BigDerivedClass(*this); }
};
class A
{
BigBaseClass *ptr;
public:
explicit A(BigBaseClass* ob);
~A() { delete ptr; }
};
A::A(BigBaseClass* ob) : ptr(ob)
{
}
int main()
{
A a(new BigDerivedClass);
}
You might think that move semantics would be a good idea, but that doesn't really work in this case since BigBaseClass is a base class, and moving a BigDerivedClass into a BigBaseClass would only move the BigBaseClass parts. But using a smart pointer would be good idea too, unless you are sure that the rest of your code is exception-free.

A canonical way to implement move semantics in a class

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.

Template syntax to accept an argument with the type of the derived object

I have a class template in which there is a function that takes an argument of the type of the class that inherits the template. I can't seem to find the right syntax to bring this about. For example, if this is my template:
// A template to promote a simple C-style struct, T_C,
// to a C++ class 'T' with constructors, destructors,
// copy and move assignment operators.
template <typename T_C> class myTemplate
{
// We could in principle create templated copy and move
// constructors and assignment operators, but they'd be
// implicitly deleted by the compiler unless we explicitly defaulted
// them: see
// http://stackoverflow.com/questions/25246573/copy-assignment-operator-defined-in-template-being-deleted-by-compiler.
// Explicitly defaulting them works well if the template is
// contained in an all-header library. However, on including
// the template-derived classes into a DLL in Visual Studio 2013
// it was found that, even when all the other methods
// in a class were properly exported, the defaulted
// template methods were not. There may be a workaround for
// this, but in the scheme of things it was thought easier
// just to dispense with the templated operators and to
// create copy and move assignment operators, as well as the
// corresponding constructors, for each derived type.
//
// We can at least simplify things a little, and force some
// consistency upon our various class definitions,
// by insisting that every relevant class defines the
// functions listed, which can then be called from the
// constructors and assignment operators to make them a bit
// more manageable.
// Tidy up all pointers etc. and return all buffers to a safe state
virtual void clear() = 0;
protected:
// Construct a 'T' from a T_C:
virtual void construct_contents(const T_C &source) = 0;
// Deep copy the contents of a T_C to a 'T'
virtual void copy_contents(const T_C &source) = 0;
// Move the contents of one object to another: for use in both the move constructor
// and the move assignment operator:
virtual void move_contents(myTemplate<T_C> &&source) = 0;
// For sure this is wrong, but I can't figure out
// what the right argument type should be
};
...and this is my class
class myClass : public myStruct, public myTemplate<myStruct>
{
public:
// Default constructor
myClass() {}
// Copy constructor taking basic C struct
myClass(const myStruct &source)
{ construct_contents(source); }
// Copy constructor taking this promoted C++ class
myClass(const myClass &source)
{ construct_contents(source); }
// Copy assignment operator taking basic C struct
MyClass & operator=(const myStruct &source)
{
copy_contents(source);
return *this;
}
// Copy assignment operator taking this promoted C++ class
MyClass & operator=(const myClass &source)
{
copy_contents(source);
return *this;
}
// Move constructor taking this promoted C++ class
myClass(myClass &&source)
{
move_contents(std::move(source));
}
// Move assignment operator taking this promoted C++ class
myClass & operator=(myClass &&source)
{
if (this != &source)
{
clear();
move_contents(std::move(source));
}
return *this;
}
// Destructor
~myClass()
{
clear();
}
// Various getters and setters for the data fields of myStruct
// ....
virtual void clear() override
{
// Stuff...
}
protected:
virtual void construct_contents(const myStruct &source) override
{
// Stuff...
}
virtual void copy_contents(const myStruct &source) override
{
// Stuff...
}
virtual void move_contents(myClass &&source) override
{
// Stuff...
}
};
...then everything works just fine except that the compiler throws an error saying (in Visual Studio 2013) 'member function declared with "override" does not override a base class member' when it encounters my definition of myClass::move_contents. I can understand why it is unhappy: 'myClass' is not only a 'myTemplate<myStruct>', but a type that jointly inherits both myTemplate<myStruct> and myStruct. What I actually need to do is to declare move_contents in the template so that the argument type is the same as the type of the derived object, in this case 'myClass'. However, I can't think of a way of specifying this in the template declaration.
My working solution is to take move_contents out of the template altogether and define a totally independent move_contents method for each separate derived type. This is all right, but doing this would block off the possibilty of pulling the move constructor and assignment operator back into the template - something I'd love to do if I can ever figure out a way of getting Visual Studio 2013 to export them properly.
The template parameter is myStruct, so the method expects an argument of type myTemplate<myStruct>, not myClass.
Declare your method with :
virtual void move_contents(myTemplate<myStruct> &&source) override
{
// Stuff...
}
I'm going to accept #quantdev's answer because he put me on the right track, but here is what eventually worked.
In my original code snippet (see my question above), I inserted an additional public virtual function into the template:
virtual T_C * baseObject() = 0;
In my definition of myClass, I overrode this as follows:
virtual myStruct * baseObject() override { return static_cast<myStruct *>(this); }
This worked because myClass inherits myStruct, so the 'this' pointer of any myClass object is always castable to a myStruct*.
Then, in my move_contents method, I was able to access the myStruct pointers to both the source and destination objects:
void move_contents(myTemplate<myStruct> && source) override
{
stuff = source.baseObject() -> stuff;
source.baseObject() -> stuff = nullptr;
// So that the buffer which my new object now
// points to isn't released when the source
// object is destroyed
}
As far as I can see, this does everything that I need.

Didn't understand Mr. Stroustup's suggestion to delete the copy default and move operations for the abstract class Shape

I'm trying to understand what the author suggests in 3.3.4 Suppressing Operations in his new book (TCPL 4th edition), to no avail.
Excerpt from book
Using the default copy or move for a class in a hierarchy is
typically a disaster: Given only a pointer to a base, we simply don’t
know what members the derived class has (§3.3.3), so we can’t know how
to copy them. So, the best thing to do is usually to delete the
default copy and move operations; that is, to eliminate to default
definitions of those two operations:
class Shape {
public:
Shape(const Shape&) =delete; // no copy operations
Shape& operator=(const Shape&) =delete;
Shape(Shape&&) =delete; //no move operations
Shape& operator=(Shape&&) =delete;
~Shape();
};
Now an attempt to copy a Shape will be caught by the compiler. If you
need to copy an object in a class hierarchy, write some kind of clone
function (§22.2.4).
For example, the code below doesn't compile with Shape(const Shape&) = delete;, as the clone() function invokes Shape's copy constructor.
#include <iostream>
class Shape
{
public:
virtual ~Shape() {}
Shape() {}
Shape(const Shape&) {};
virtual Shape* clone() const = 0;
};
class Circle: public Shape
{
public:
Circle(int i) : a(i) {}
Circle* clone() const { return new Circle(*this); }
int a;
};
int main()
{
Shape* p = new Circle(1);
Shape* q = p->clone();
std::cout << dynamic_cast<Circle*>(p)->a << std::endl;
std::cout << dynamic_cast<Circle*>(q)->a << std::endl;
}
If you only have a pointer to a Shape, then you can't possibly make a copy of the actual implementation - it will (most likely) be larger, so your copy will be "sliced". In your example, Circle will have an extra int a; that isn't part of the Shape class - which would be lost if you just plain copy a Shape class object without knowing it's a Circle (and the whole point of polymorphism is that you are not supposed to "know" what object is what type when dealing with it in generic functions)
To avoid having problems caused by accidentally using something like:
*q = *p;
it is better to "delete" the operators that will allow you to do that
However, as the copy constructor is needed for the case you describe, so one solution is to make it protected - that protects against something other than a derived class using it, and works correctly.
Thanks to robson below (and a nights sleep as well), the solution is clearly to make a copy-constructor in Circle. Just because you don't have one for Shape doesn't mean that you can't have one in the derived class:
class Circle: public Shape
{
public:
Circle(int i) : a(i) {}
Circle(const Circle& other) { a = other.a; } // Note this line!
Circle* clone() const { return new Circle(*this); }
int a;
};
The reason it tries to use the Shape copy constructor is because you haven't got one in your own class. You should!
You could also do (as Robson explained):
class Circle: public Shape
{
public:
Circle(int i) : a(i) {}
Circle* clone() const { return new Circle(a); }
int a;
};
And not need a copy constructor at all. Both of these solutions solve the "you are trying to use a deleted Shape(const Shape &) constructor. It really is obvious once you see it.
He meant that its bad to make them accessible from outside, due to potentioal object slicing problems. It you dont need to make the class clonable than deleting is sufficient, otherwise you can just make it protected to be accessible only within clone() and successors.

How to clone object in C++ ? Or Is there another solution?

I wrote a Stack and Queue implementation (Linked List based). There is one stack (bigStack). For example, I separate bigStack (example: stackA and stackB). I pop() a node from bigStack, I push() in stackA. In the same way, I push() in stackB. I want bigStack to not change. Therefore I want to clone the bigStack object. How do I clone objects in C++? Or is there another solution to my problem?
class Stack : public List {
public:
Stack() {}
Stack(const Stack& rhs) {}
Stack& operator=(const Stack& rhs) {};
~Stack() {}
int Top() {
if (head == NULL) {
cout << "Error: The stack is empty." << endl;
return -1;
} else {
return head->nosu;
}
}
void Push(int nosu, string adi, string soyadi, string bolumu) {
InsertNode(0, nosu, adi, soyadi, bolumu);
}
int Pop() {
if (head == NULL) {
cout << "Error: The stack is empty." << endl;
return -1;
} else {
int val = head->nosu;
DeleteNode(val);
return val;
}
}
void DisplayStack(void);
};
then...
Stack copyStack = veriYapilariDersi;
copyStack.DisplayStack();
The typical solution to this is to write your own function to clone an object. If you are able to provide copy constructors and copy assignement operators, this may be as far as you need to go.
class Foo
{
public:
Foo();
Foo(const Foo& rhs) { /* copy construction from rhs*/ }
Foo& operator=(const Foo& rhs) {};
};
// ...
Foo orig;
Foo copy = orig; // clones orig if implemented correctly
Sometimes it is beneficial to provide an explicit clone() method, especially for polymorphic classes.
class Interface
{
public:
virtual Interface* clone() const = 0;
};
class Foo : public Interface
{
public:
Interface* clone() const { return new Foo(*this); }
};
class Bar : public Interface
{
public:
Interface* clone() const { return new Bar(*this); }
};
Interface* my_foo = /* somehow construct either a Foo or a Bar */;
Interface* copy = my_foo->clone();
EDIT: Since Stack has no member variables, there's nothing to do in the copy constructor or copy assignment operator to initialize Stack's members from the so-called "right hand side" (rhs). However, you still need to ensure that any base classes are given the opportunity to initialize their members.
You do this by calling the base class:
Stack(const Stack& rhs)
: List(rhs) // calls copy ctor of List class
{
}
Stack& operator=(const Stack& rhs)
{
List::operator=(rhs);
return * this;
};
In C++ copying the object means cloning. There is no any special cloning in the language.
As the standard suggests, after copying you should have 2 identical copies of the same object.
There are 2 types of copying: copy constructor when you create object on a non initialized space and copy operator where you need to release the old state of the object (that is expected to be valid) before setting the new state.
If your object is not polymorphic (and a stack implementation likely isn't), then as per other answers here, what you want is the copy constructor. Please note that there are differences between copy construction and assignment in C++; if you want both behaviors (and the default versions don't fit your needs), you'll have to implement both functions.
If your object is polymorphic, then slicing can be an issue and you might need to jump through some extra hoops to do proper copying. Sometimes people use as virtual method called clone() as a helper for polymorphic copying.
Finally, note that getting copying and assignment right, if you need to replace the default versions, is actually quite difficult. It is usually better to set up your objects (via RAII) in such a way that the default versions of copy/assign do what you want them to do. I highly recommend you look at Meyer's Effective C++, especially at items 10,11,12.