CS253: Software Development with C++

Fall 2022

Rule Of Three

Show Lecture.RuleOfThree as a slide show.

CS253 Rule Of Three

The Rule of Three

The Rule of Three states that if a class defines any of these methods, called the “Big Three”, then it should probably define all of them:

There are certainly exceptions to this rule. Fortunately, the rule says “probably”.

The C++ standard nudges you along

The C++11 standard states:

The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor (15.4, 15.8). In a future revision of this International Standard, these implicit definitions could become deleted (11.4).

In other words,
• Write a copy ctor if you wrote operator= or a dtor.
• Write an operator= if you wrote a copy ctor or a dtor.
because we might stop providing them someday.

When none of the Big Three are needed

class Hero {
    string name;
  public:
    Hero(string n) : name(n) { }
    auto who() const { return name; }
};
Hero h1("Superman"), h2(h1);
cout << h1.who() << ' ' << h2.who();
Superman Superman

No problem

Problem

class Hero {
    char *name;
  public:
    Hero(const char *n) : name(new char[strlen(n)+1]) {
        copy(n, n+strlen(n)+1, name);
    }
    ~Hero() { delete[] name; }
    auto who() const { return name; }
};
Hero h1("Superman");
cout << h1.who();
Superman

It works well so far. Let’s try using the compiler-generated copy ctor.

Problem

class Hero {
    char *name;
  public:
    Hero(const char *n) : name(new char[strlen(n)+1]) {
        copy(n, n+strlen(n)+1, name);
    }
    ~Hero() { delete[] name; }
    auto who() const { return name; }
};
Hero h1("Superman"), h2(h1);
cout << h1.who() << ' ' << h2.who() << endl;
Superman Superman
free(): double free detected in tcache 2
SIGABRT: Aborted

Problem with copy ctor

Problem with assignment operator

Solution #1

class Hero {
    char *name;
  public:
    Hero(const char *n) : name(new char[strlen(n)+1]) {
        copy(n, n+strlen(n)+1, name);
    }
    Hero(const Hero &rhs) : name(new char[strlen(rhs.name)+1]) {
        copy(rhs.name, rhs.name+strlen(rhs.name)+1, name);
    }
    Hero& operator=(const Hero &rhs) {
        delete[] name;
        name = new char[strlen(rhs.name)+1];
        copy(rhs.name, rhs.name+strlen(rhs.name)+1, name);
        return *this;
    }
    ~Hero() { delete[] name; }
    auto who() const { return name; }
};
Hero h1("Superman"), h2(h1);
cout << h1.who() << ' ' << h2.who();
Superman Superman

Solution #2

class Hero {
    char *name = nullptr;
    void set(const char *p) {
        delete[] name;
        name = new char[strlen(p)+1];
        copy(p, p+strlen(p)+1, name);
    }
  public:
    Hero(const char *n) { set(n); }
    Hero(const Hero &rhs) { set(rhs.name); }
    Hero& operator=(const Hero &rhs) {
        set(rhs.name);
        return *this;
    }
    ~Hero() { delete[] name; }
    auto who() const { return name; }
};
Hero h1("Superman"), h2(h1);
cout << h1.who() << ' ' << h2.who();
Superman Superman

It gets worse

Move methods

string where = "base", what = "ball";
string sport = where+what;
cout << sport;
baseball

Move methods