T B H P N

C++ Class Structure

Synopsis:

The C++ language was built, from the ground up, to support user defined value types.

It does that with copy constructors and copy assignment operators that allow the designer to implement deep copies. That makes the state of the destination object have the same value as the state of the source, but those states are independent. If the source changes its state later, that does not affect the state of the destination.

Code in Header File X.h

  class X {
  public:
    X();                        // default constructor
    x(const std::string& msg);  // another constructor
    X(const X& x);              // copy constructor
    X& operator=(const X& x);   // copy assignment operator
    ~X();                       // destructor
    std::string getMessage();   // method
  private:
    std::string* pMsg;          // private data
  };
      

Code in Implementation File X.cpp

  X::X() : pMsg(new std::string()) {}
  X::X(const std::string& s) : pMsg(new std::string(s)) {}
  X::X(const X& x) : pMsg(new std::string(*x.pMsg)) {}
  X& X::operator=(const X& x)
  {
    if(this != &x)
    {
      delete pMsg;
      pMsg = new std::string(*x.pMsg);
    }
    return *this;
  }
  X::~X()
  {
    delete pMsg;
  }
  std::string getMessage()
  {
    return *pMsg;
  }
      

C++ provides destructors that are called when the thread of execution leaves the scope where the instance was declared. This means that developers have tight control over how and when an instance's resources are returned to the process, for use by other instances. Users of C++ classes do not need to be aware of these resource allocations and deallocations. They simply use instances of the classes and the instances take care of all resource management.

The language also provides move construction and move assignment operations. Move construction builds an instance, taking ownership of another instance's state. This transfer is very efficient, usually effected with a pointer swap. Similarly, move assignment discards the destination's state and it takes ownership of the source's state.

Move operations usually occur only when the source is a temporary object, but the compiler can be coerced to execute a move on a non-temporary object by using the std::move(...) operation. Note that you need to be careful with this, as the source no longer owns it's former state and may be unable to continue. Essentially, the use of the std::move(...) is a promise not to use the source object any longer, or to reinitialize it in some effective way.

Construction-Destruction Guarantee:

Instances of C++ classes hold instances of their base classes and composed member data in their memory footprint. That means that a class's bases and composed members are an integral part of the class, and are always constructed when a class instance is constructed, and destroyed when it is destroyed. C++ Guarantees that! There is no way to prevent it.

For this reason, the language has to ensure that is always possible. It does so by providing compiler generated operations.

Compiler Generated Operations:

C++ Compilers will generate construction and destruction operations if not provided by the class.

For every class you define, you should decide whether to provide construction, assignment, and destruction operations, or allow the compiler to generate them, or to disable those operations.

The Rule of Threes:

To summarize, for the three operations of construction, assignment, and destruction, we have three choices:

  • Allow the compiler to generate them - don't declare them - if all the classes bases and members have correct construction, assignment, and destruction semantics.
  • Provide them by declaring and implementing each of the operations.
  • Disable them by declaring each of the operations using = delete at the end of the declaration.

Example Code:

CST strip