constructor

Constructors

#include <iostream>
#include <utility>

class C {
 public:
  // constructor
  C(int x) : x_(x) {}

  // default constructor
  C() = default;

  // copy constructor
  C(const C &other) : C(other.x_) {
    std::cout << "copy constructor\n";
  }

  // copy assignment
  C &operator=(const C &other) {
    std::cout << "copy assignment\n";
    x_ = other.x_;
    return *this;
  }

  // move constructor
  C(C &&other) : x_(std::move(other.x_)) {
    std::cout << "move constructor\n";
    other.x_ = 0;
  }

  // move assignment
  C &operator=(C &&other) {
    std::cout << "move assignment\n";
    x_ = std::move(other.x_);
    return *this;
  }

 private:
  int x_;
};

int main(int argc, char *argv[]) {
  C c1;                   // call default constructor
  C c2(1);                // call constructor
  C c3 = C(2);            // call constructor
  C c4(c2);               // call copy constructor
  C c5(std::move(C(2)));  // call move constructor
  C c6 = c1;              // call copy constructor
  C c7 = std::move(C(2)); // call move constructor
  C c8 = std::move(c3);   // call move constructor

  C c9;
  C c10;

  c9 = c2;                // call copy assignment
  c10 = std::move(c4);    // call move assignment
  c10 = C(2);             // call move assignment
}

Rule of three

#include <iostream>
#include <memory>
#include <string>
#include <cstring>

class RuleOfThree {
 public:
  RuleOfThree(const char *s, size_t n)
   : cstr_(new char[n])
   , n_(n) {
    memcpy(cstr_, s, n);
  }

  // if we have a user-defined destructor
  ~RuleOfThree() { delete[] cstr_; }
  // we need one a user-defined copy constructor
  RuleOfThree(const RuleOfThree &other)
    : RuleOfThree(other.cstr_, other.n_) {}
  // and user-defined copy assignment
  RuleOfThree &operator=(const RuleOfThree &other) {
    if (this == std::addressof(other)) {
      return *this;
    }
    delete[] cstr_;
    n_ = other.n_;
    cstr_ = new char[other.n_];
    memcpy(cstr_, other.cstr_, n_);
    return *this;
  }

  friend std::ostream &operator<<(std::ostream &os, const RuleOfThree &);

 private:
  char *cstr_;
  size_t n_;
};

std::ostream &operator<<(std::ostream &os, const RuleOfThree &r) {
  return os << r.cstr_;
}

int main(int argc, char *argv[]) {
  std::string s = "Rule of three";
  RuleOfThree r3(s.c_str(), s.size() + 1);
  std::cout << r3 << "\n";
}

Rule of five

#include <iostream>
#include <memory>
#include <string>
#include <cstring>
#include <utility>

class RuleOfFive {
 public:
  RuleOfFive(const char *s, int n) : cstr_(new char[n]) {
    std::memcpy(cstr_, s, n);
  }

  // if there is a user-defined destructor including default or delete
  ~RuleOfFive() { delete[] cstr_; }
  // a user-defined copy constructor
  RuleOfFive(const RuleOfFive &other)
    : RuleOfFive(other.cstr_, strlen(other.cstr_) + 1) {}
  // a user-defined move constructor
  RuleOfFive(RuleOfFive &&other)
    : cstr_(std::exchange(other.cstr_, nullptr)) {}
  // a user-define copy assignment
  RuleOfFive &operator=(const RuleOfFive &other) {
    return *this = RuleOfFive(other);
  }
  // a user-defined move assignment have to declare explicitly.
  RuleOfFive &operator=(RuleOfFive &&other) {
    std::swap(cstr_, other.cstr_);
    return *this;
  }

  friend std::ostream &operator<<(std::ostream &os, const RuleOfFive &);

 private:
  char *cstr_;
};

std::ostream &operator<<(std::ostream &os, const RuleOfFive &r5) {
  return os << r5.cstr_;
}

int main(int argc, char *argv[]) {
  std::string s = "Rule of five";
  RuleOfFive r5(s.c_str(), s.size() + 1);
  std::cout << r5 << "\n";
}

Rule of zero

#include <iostream>
#include <string>

class RuleOfZero {
 public:
  RuleOfZero(const std::string &s) : s_(s) {}
  // if we don't have a user-defined destructor, we should not have
  // user-defined copy/move constructors or copy/move assignment.
  friend std::ostream &operator<<(std::ostream &os, const RuleOfZero &r0);
 private:
  const std::string s_;
};

std::ostream &operator<<(std::ostream &os, const RuleOfZero &r0) {
  return os << r0.s_;
}

int main(int argc, char *argv[]) {
  RuleOfZero r0("Rule of zero");
  std::cout << r0 << "\n";
}

Note that a polymorphic class should supress public copy/move.

#include <iostream>
#include <string>
#include <utility>

// bad
class A {
 public:
  virtual std::string f() { return "a"; }
};

class B : public A {
 public:
  std::string f() override { return "b"; }
};

void func(A &a) {
  auto c = a;
  std::cout << c.f() << "\n";
}

int main(int argc, char *argv[]) {
  B b;
  func(b);
}
#include <iostream>
#include <string>
#include <utility>

class A {
 public:
  A() = default;
  A(const A&) = delete;
  A &operator=(const A&) = delete;
  virtual std::string f() { return "a"; }
};

class B : public A {
 public:
  std::string f() override { return "b"; }
};

void func(A &a) {
  auto c = a;  // compile error here!
  std::cout << c.f() << "\n";
}

int main(int argc, char *argv[]) {
  B b;
  func(b);
}