“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201055
Explicitly Disallow UndesiredCompiler-generated Functions. As mentioned, the compiler generates copy
constructor and copy assignment automatically for us.In some cases, however, we do not want them.
class CPlayer {public:
CPlayer() {uniqueID_ = NameDatabase_.GetID();
}// ...
private:static CNameDatabase NameDatabase_;int uniqueID_;// ...
};int main() {
CPlayer p1, p2(p1), p3;p3 = p1;
}
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201056
Explicitly Disallow UndesiredCompiler-generated Functions. Solution: Define copy constructor and copy
assignment definitely and make them private.
class CPlayer {public:
CPlayer() {uniqueID_ = NameDatabase_.GetID();
}// ...
private:CPlayer(const CPlayer &) {}CPlayer & operator = (const CPlayer &) {}static CNameDatabase NameDatabase_;int uniqueID_;// ...
};int main() {
CPlayer p1, p2(p1), p3;p3 = p1;
} Compilation error
Is it completely safe?(Who can invoke the private member functions?)
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201057
Explicitly Disallow UndesiredCompiler-generated Functions. A problem that is not solved by our solution.
class CPlayer {public:
CPlayer() {uniqueID_ = NameDatabase_.GetID();
}void Accident() {
CPlayer p1, p2(p1), p3;p3 = p1;
}// ...
private:CPlayer(const CPlayer &) {}CPlayer & operator = (const CPlayer &) {}static CNameDatabase NameDatabase_;int uniqueID_;// ...
};
Copying is still allowed in the member function(and CPlayer’s friends).
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201058
Explicitly Disallow UndesiredCompiler-generated Functions. Solution+: Just declare them, but do not define them.
class CPlayer {public:
CPlayer() {uniqueID_ = NameDatabase_.GetID();
}void Accident() {
CPlayer p1, p2(p1), p3;p3 = p1;
}// ...
private:CPlayer(const CPlayer &);CPlayer & operator = (const CPlayer &);static CNameDatabase NameDatabase_;int uniqueID_;// ...
};
Linkage error
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201059
Explicitly Disallow UndesiredCompiler-generated Functions. Solution++: Define a base class to avoid copying.
All copying actions will cause errors during compilation.(It is better to detect the errors earlier.)
class Uncopyable {protected:
Uncopyable() {}~Uncopyable() {}
private:Uncopyable(const Uncopyable &);Uncopyable& operator = (const Uncopyable &);
};class CPlayer : private Uncopyable {
// Do not define copy constructor & copy assignment
};
FAQ1: Why not public or private?
FAQ2: Why not public?
FAQ1: Because we do not want to define Uncopyable objects but want to define CPlayer objects.FAQ2: private inheritance means “is implemented in terms of”, where public inheritance means “is a”.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201060
Explicitly Disallow UndesiredCompiler-generated Functions. Summary:
Ensure your class provides correct copying actions,you have the following options: Explicitly disable copying. Explicitly define your own copying functions. Use the compiler-generated version, AND use comments to
let readers know that you do not ignore the above twooptions carelessly.
Note: Copying & standard containers Disable copying implies that you can not put your objects
into standard containers. However, you can still simulate it through the aid of (smart)
pointers.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201061
Prefer the Canonical Form ofAssignment. Let your operator= be a member function with one
of the following two signatures*:T& operator=(const T&); // classic
T& operator=(T); // if you want a copy of the argument
Return a reference to *this. Don’t return const T&.
Copy all parts of an object.
Handle assignment to self. Avoid making your assignment operator virtual.
If you really need it, prefer to provide a named functioninstead (e.g. virtual void Assign(const T&); )
* An exception is std::auto_ptr. (See item 10 in C++ Gotchas.)
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201062
Prefer the Canonical Form ofAssignment.Return a reference to *this. Don’t return const T&.class CPlayer {public:
CPlayer & operator = (const CPlayer &) { // ...return *this;
}};void func(int &) { // ...}void func(CPlayer &) { // ...}int main(){
int n1, n2, n3;n1 = n2 = n3;func(n1 = n2);
CPlayer p1, p2, p3;p1 = p2 = p3;func(p1 = p2);return 0;
}
Reason:Since this behavior is followed by all primitivetypes and STL types, just follow it.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201063
Prefer the Canonical Form ofAssignment.Copy all parts of an object.
class CPlayer {public:// no copy assignment is defined.private:
std::string name_;int money_;
// ...};int main(){
CPlayer p1, p2;p1 = p2;
}
class CPlayer {public:CPlayer & operator= (const CPlayer &rhs){
name_ = rhs.name_;money_ = rhs.money_;return *this;
}private:
std::string name_;int money_;
// ...};int main(){
CPlayer p1, p2;p1 = p2;
}
Compiler-generated copying functionswork well.
When you define your own copyingfunctions (and ctors), remember toupdate them when you add newdata members.
// add a new memberint someValue_;
// add a new memberint someValue_;
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201064
Prefer the Canonical Form ofAssignment.Copy all parts of an object.
class CPowerPlayer : public CPlayer {public:
CPowerPlayer():power_(0) {
}CPowerPlayer(const CPowerPlayer &rhs)
:power_(rhs.power_) {}CPowerPlayer & operator = (const CPowerPlayer &rhs) {
power_ = rhs.power_;}
private:int power_;
};int main(){
CPowerPlayer pp1;pp1.SetName(“Jeter”);CPowerPlayer pp2(pp1), pp3;pp3 = pp1;
}
class CPlayer {private:
std::string name_;int money_;
};
What’s wrong with them?
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201065
Prefer the Canonical Form ofAssignment.Copy all parts of an object.
class CPowerPlayer : public CPlayer {public:
// ...CPowerPlayer(const CPowerPlayer &rhs)
:CPlayer(rhs),power_(rhs.power_) {
}CPowerPlayer & operator = (const CPowerPlayer &rhs) {
CPlayer::operator=(rhs);power_ = rhs.power_;
}private:
int power_;};int main(){
CPowerPlayer pp1;pp1.SetName(“Jeter”);CPowerPlayer pp2(pp1), pp3;pp3 = pp1;
}
When you write a copying function, ensure that(1) copy all local data members(2) call suitable copying functions for all base
classes.
(In this example, we don’t need to write the twocopying functions by ourselves. The compiler’sversion is fine.)
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201066
Prefer the Canonical Form ofAssignment.Copy all parts of an object.
class CPowerPlayer : public CPlayer{public:
CPowerPlayer():power_(0) {}// CPowerPlayer(const CPowerPlayer &rhs) {}// CPowerPlayer & operator = (const CPowerPlayer &) {}
int power_;};
int main(){
CPowerPlayer pp1;pp1.name_ = "pp1";CPowerPlayer pp2(pp1), pp3;pp3 = pp1;cout << pp2.name_ << ' ' << pp3.name_ << endl;return 0;
}
class CPlayer{public:
CPlayer():name_("default"),money_(0) {}
std::string name_;int money_;
};
Before remarking: default defaultAfter remarking: pp1 pp1
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201068
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Am I stupid to do self-assignment?
int main(){
CPlayer me, you;CPlayer players[5];CPlayer *ptr1, *ptr2;CPlayer &ref = you;int i, j;
// ...
me = me; // I am not that stupid.players[i] = players[j]; // Am I sure that i j?*ptr1 = *ptr2; // Am I sure that ptr1 ptr2?ref = *ptr1; // Am I sure that ref *ptr1?
return 0;}
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201069
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Wrong implementation –version 1.0
class CImage { ... };
class CPlayer {public:
CPlayer & operator = (const CPlayer &rhs);private:
CImage *image_;// ...};
CPlayer & CPlayer::operator= (const CPlayer &rhs){
image_ = new CImage(*rhs.image_);return *this;
} memory leak
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201070
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Wrong implementation –version 1.1
class CImage { ... };
class CPlayer {public:
CPlayer & operator = (const CPlayer &rhs);private:
CImage *image_;// ...};
CPlayer & CPlayer::operator= (const CPlayer &rhs){
delete image_;image_ = new CImage(*rhs.image_);return *this;
} no dealing with self-assignment
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201071
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Wrong implementation –version 2
class CImage { ... };
class CPlayer {public:
CPlayer & operator = (const CPlayer &rhs);private:
CImage *image_;// ...};
CPlayer & CPlayer::operator= (const CPlayer &rhs){
if (this == &rhs) return *this;
delete image_;image_ = new CImage(*rhs.image_);return *this;
}not exception-safe(what if new CImage throws exception?)
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010
Prefer the Canonical Form ofAssignment.#include <iostream>#include <new>using namespace std;
void MemAlloc(char **p, int t){
cout << "Before allocation..." << endl;*p = new char [1024*1024*500]; // 500 MB at a timecout << "Allocate " << 500*t << " MB successfully." << endl;
}
int main(){
const int TrialNum = 100;char *p[TrialNum];for (int i=1; i<=TrialNum; i+=1){
try {MemAlloc(&p[i], i);
}catch (bad_alloc &ba) {
cout << "Memory allocation failed." << endl;break;
}cout << "Back from MemAlloc.\n" << endl;
}}
Before allocation...Allocate 500 MB successfully.Back from MemAlloc.
Before allocation...Allocate 1000 MB successfully.Back from MemAlloc.
Before allocation...Allocate 1500 MB successfully.Back from MemAlloc.
Before allocation...Memory allocation failed.
An example of exception caused byunsuccessful memory allocation.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201073
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Correct implementation –version 1
class CImage { ... };
class CPlayer {public:
CPlayer & operator = (const CPlayer &rhs);private:
CImage *image_;// ...};
CPlayer & CPlayer::operator= (const CPlayer &rhs){
CImage *pOrig = image_;image_ = new CImage(*rhs.image_);delete pOrig;return *this;
}self-assignment safeexception safe
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201074
Prefer the Canonical Form ofAssignment.Handle assignment to self.
Correct(?) implementation –version 1 (Is it still exception-safe?)
class CImage { ... };
class CPlayer {public:
CPlayer & operator = (const CPlayer &rhs);private:
CImage *image1_, *image2_;// ...};CPlayer & CPlayer::operator= (const CPlayer &rhs){
CImage *pOrig = image1_;image1_ = new CImage(*rhs.image1_);delete pOrig;pOrig = image2_;image2_ = new CImage(*rhs.image2_);delete pOrig;return *this;
}not strong exception-safe(what if new CImage(*rhs.image2) throws exception?)
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201075
Prefer the Canonical Form ofAssignment.Handle assignment to self.
The most recommended implementation (copy and swap tech.)
class CPlayer {public:
CPlayer(const CPlayer &rhs) { ... }CPlayer & operator = (const CPlayer &rhs);void swap(CPlayer &rhs) { ... }
// ...};CPlayer & CPlayer::operator= (const CPlayer &rhs){
CPlayer temp(rhs);swap(temp);return *this;
}
In principle, the swap() function should be no-fail.
If self-assignment is frequent due to reference aliasing or other reasons, it’s okay to still checkfor self-assignment anyway as an optimization check to avoid needless work.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010
Exercise
Add more operators to your simulated Stringclass.
class String{public:
// 1. default constructor// 2. copy constructor// 3. constructor with one parameter with type const char *// 4. destructor// 5. size()// 6. c_str()// 7. copy assignment// 8. operator []// 9. operator +=
private:char *str_;
};// A. operator <// B. operator <<
76
Top Related