# Classes, Constructors and Destructors — C++

Source: https://www.skillbyai.com/en/cpp/o-classes

> Write classes with member initialiser lists, const member functions and destructors.

## User-defined types done well

A **class** (or `struct`, which is identical except members are public by default) groups data and functions. Members are `private` by default in a class; expose a small `public` interface. **Constructors** initialise objects; use the **member initialiser list** (`: name_(name), balance_(0)`) rather than assignments in the body, because members are initialised **before** the body runs, `const` and reference members can only be set there, and it avoids a default construction followed by assignment. Members are initialised in the **order they are declared**, not the order written in the list, which compilers warn about. Mark single-argument constructors **`explicit`** to prevent surprising implicit conversions. **Default member initialisers** (`int count_{0};`) set defaults in one place. A **destructor** (`~ClassName()`) runs when the object's lifetime ends and releases resources. Mark member functions that do not modify the object **`const`** (`double balance() const;`), so they can be called on const objects and const references. Use `= default` and `= delete` to request or forbid special member functions explicitly.

## Lifetime of an object

Construction initialises members in declaration order; destruction runs in reverse when the scope ends.

![A horizontal timeline with a building block assembling at the start and dismantling at the end, small arrows showing reverse order.](assets/figures/cpp/section-4-map.svg) — Figure 4.1 — Construction and destruction order.

## A well-formed class

Initialiser list, explicit constructor, const member functions and invariants.

```cpp
#include <stdexcept>
#include <string>
#include <utility>

class Account {
public:
    explicit Account(std::string owner, long long opening_paise = 0)
        : owner_(std::move(owner)), balance_(opening_paise) {
        if (balance_ < 0) throw std::invalid_argument("opening balance must be >= 0");
    }

    void deposit(long long paise) {
        if (paise <= 0) throw std::invalid_argument("deposit must be positive");
        balance_ += paise;
    }

    bool withdraw(long long paise) {
        if (paise <= 0 || paise > balance_) return false;
        balance_ -= paise;
        return true;
    }

    [[nodiscard]] long long balance() const { return balance_; }       // const: does not modify
    [[nodiscard]] const std::string& owner() const { return owner_; }

private:
    std::string owner_;
    long long balance_{0};
};

// Account a = "Asha";   // error: constructor is explicit (no accidental conversion)
Account a{"Asha", 50'000};
```

## Initialise in declaration order

If `balance_` is declared before `owner_` but your initialiser list uses `owner_` to compute `balance_`, you read an uninitialised member. Keep the list in the same order as the declarations and enable warnings such as `-Wreorder`.

**Quiz:** Why is a member initialiser list preferred over assigning members in the constructor body?

- [ ] It is required for all members
- [ ] It makes members public
- [x] Members are initialised directly instead of being default-constructed then assigned, and const or reference members require it
- [ ] It runs after the constructor body

*Answer:* Members are initialised directly instead of being default-constructed then assigned, and const or reference members require it. Initialiser lists initialise members once, correctly, before the body runs.
