Part of the task I have is to create a class, which contains two lists of two other classes and create "The big 4"(constructor, copy constructor, operator=, destructor)
Here's what I did:
using namespace std;
class A{...};
class B{...};
class C{
list<A> a;
list<B> b;
public:
C();
~C();
C(const C& c);
void operator=(const C& c);
};
C::C(){
//How to allocate memory for a and b?
}
C::~C(){
//How to free the memory?
}
C::C(const C& c){
a=c.a;
b=c.b;
}
void operator=(const C& c){
if(&c==this) return;
// how do I delete a and b?
a=c.a;
b=c.b;
}
Could you clear out the things that I don't understand(as comments in the code). And also give advice if I haven't done anything correctly?
Since you're using values (std::list values), there is nothing to do. Your constructor will call the std::list constructors automatically, which allocate any resources needed. Your destructor will call the std::list destructor, which frees resources which it acquired.
You would need some extra work if you either hold pointers to lists (i.e. std::list<A> *a;) or lists of pointers (std::list<A*> a;') - or both (std::list<A*> *a;).
//How to allocate memory for a and b?
You don't need to allocate memory by yourself. because you are allocating your member variables on stack. which is automatically set up when someone instanciate's your class.
//How to free the memory?
You don't need to free memory by yourself. for the same reason. your stack variables will be automatically deallocated when scop ends.
// how do I delete a and b?
Why do you need to delete a and b ? you are copying the data from other object to this.
if you use std::list, your class A or B should:
1 can be allocate on stack and heap
2 can not be a singleton object.
Your variables are using the auto storage class they do not need to be explicitly deleted.
Your assignment operator
void operator=(const& C rhs);
shouldn't return void. // as this is not what the built in types do and prevents assignment chaining.
e.g.
int x, y, z;
x = y = z = 0;
It looks like your trying to implement move semantics with your comment
// how do I delete a and b?
your assignment operator should copy the state or you should document that it uses non-standard behavior. e.g. move semantics, boost::noncopyable Et cetera.
With the advent of the move-constructor, it is now the Big Six. Study and understand all the ins and outs here, and you will have a masters degree in class boiler plates.
#include <list>
class A {};
class B {};
class C{
std::list<A> a;
std::list<B> b;
public:
typedef std::list<A>::size_type size_type;
explicit C(size_type sz =0);
virtual ~C();
C(const C& c);
// Specialize external swap. Necessary for assignment operator below,
// and for ADL (argument-dependant lookup).
friend void swap(C& first, C& second);
// Assignment-operator. Note that the argument "other" is passed by value.
// This is the copy-and-swap idiom (best practice).
C& operator=(C other); // NOTE WELL. Passed by value
// move-constructor - construct-and-swap idiom (best practice)
C(C&& other);
};
C::C(size_type sz) : a(sz), b(sz) {}
C::~C(){}
C::C(const C& c) :a(c.a), b(c.b){}
void swap(C& first, C& second) {
// enable ADL (best practice)
using std::swap;
swap(first.a, second.a);
swap(first.b, second.b);
}
// Assignment-operator. Note that the argument "other" is passed by value.
// This is the copy-and-swap idiom (best practice).
C& C::operator=(C other) {
swap(*this, other); // Uses specialized swap above.
return *this;
}
// move-constructor - construct-and-swap idiom (best practice)
C::C(C&& other): a(0) , b(0) {
swap(*this, other);
}
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.
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; }
};
Lately I often reset an object by assigning a new value to it with operator=. Most of my classes have a copy constructor and operator= defined using "copy and swap" idiom. Which works fine in most cases, albeit not as efficient as it could be, but that mostly does not matter. There is one case that this does not work though. Its when the destructor needs to be called before the constructor of the new object.
Note: most of the classes for which I use this are uncopyable
class Foo
{
public:
Foo() : m_i(0) {}
Foo(int i) : m_i(i) {}
Foo(Foo&& rhs);
Foo& operator=(Foo rhs);
friend void swap(Foo& lhs, Foo& rhs);
private:
Foo(Foo& rhs) {} // uncopyable object
int m_i;
};
Foo::Foo(Foo&& rhs)
: Foo()
{
swap(*this, rhs);
}
Foo& Foo::operator=(Foo rhs)
{
swap(*this, rhs);
return *this;
}
void swap(Foo& lhs, Foo& rhs)
{
using std::swap;
swap(lhs.m_i, rhs.m_i);
}
int main()
{
Foo f(123);
f = Foo(321); // at one time both Foo(123) and Foo(321) exist in memory
}
I have then taught to maybe rewrite operator= to first manually call the destructor and then do the swap (in this case rhs would be taken by const reference). However this answer on stackOverflow made me think otherwise.
I really like operator= to reset my objects, becuase the code is clean and is the same as for built in types (like int). It also uses both the code from constructor and destructor, so no extra code needs to be written and maintained.
So my question is: Is there a way to achieve my goal to reset my object with clean code and no extra code to be written and have the object be destructed before the new one is constructed?
By definition, if you assign a new value to an old object, the new value has been constructed before the assignment can take place.
Your 'old object' is not really destructed, either.
So No. There is no way. And there shouldn't: you shouldn't redefine the 'obvious' behavior of the assignment operator.
But placement new could help here apart from the tilde and exotic construction syntax, maybe this code approaches 'clean' :)
Foo old(a, b, c);
old.~Foo(); // explicit destruction
new (&old) Foo(d, e, f);
If you have code in the constructor that needs to also be called by the assignment operator, then put that code in a private member function and call that from both the destructor and the assignment operator.
You object won't be destructed (and you don't want it to be really), but it will do the same thing as the destructor.
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.