C++ Classes

Class Definition

Members, constructor, destructor
class Host {
public:
    // Constructor
    Host(std::string name, std::string ip, int port)
        : name_(std::move(name))
        , ip_(std::move(ip))
        , port_(port)
        , active_(true) {}

    // Destructor
    ~Host() = default;

    // Methods
    std::string address() const {
        return ip_ + ":" + std::to_string(port_);
    }

    void deactivate() { active_ = false; }
    bool is_active() const { return active_; }

private:
    std::string name_;
    std::string ip_;
    int port_;
    bool active_;
};

Host h("sw01", "10.50.1.10", 22);
std::cout << h.address() << std::endl;

Member initializer lists (: name_(name)) are more efficient than assignment in the constructor body. const methods promise not to modify the object.

Access Control

public, private, protected
class Device {
public:      // accessible to anyone
    std::string name() const { return name_; }

protected:   // accessible to derived classes
    void log(const std::string& msg) {
        std::cout << "[" << name_ << "] " << msg << std::endl;
    }

private:     // accessible only within this class
    std::string name_;
    std::string ip_;
};

struct defaults to public access. class defaults to private access. Use struct for plain data, class when you have invariants to protect.

Inheritance

Single and virtual inheritance
class NetworkDevice : public Device {
public:
    NetworkDevice(std::string name, int vlan)
        : Device(std::move(name)), vlan_(vlan) {}

    virtual std::string describe() const {
        return name() + " (VLAN " + std::to_string(vlan_) + ")";
    }

    virtual ~NetworkDevice() = default;  // always virtual dtor in base

private:
    int vlan_;
};

class Switch : public NetworkDevice {
public:
    using NetworkDevice::NetworkDevice;

    std::string describe() const override {
        return "Switch: " + NetworkDevice::describe();
    }
};

virtual enables runtime polymorphism. override is a compile-time check that you are actually overriding a virtual method. Always make destructors virtual in base classes.

Rule of Five

If you define one, define all five
class Buffer {
public:
    Buffer(size_t size) : data_(new char[size]), size_(size) {}

    ~Buffer() { delete[] data_; }                              // Destructor

    Buffer(const Buffer& other)                                // Copy constructor
        : data_(new char[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + size_, data_);
    }

    Buffer& operator=(const Buffer& other) {                   // Copy assignment
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = new char[size_];
            std::copy(other.data_, other.data_ + size_, data_);
        }
        return *this;
    }

    Buffer(Buffer&& other) noexcept                            // Move constructor
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }

    Buffer& operator=(Buffer&& other) noexcept {               // Move assignment
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }

private:
    char* data_;
    size_t size_;
};

If your class manages a resource (memory, file handle, socket), you need all five special members. If it uses only RAII wrappers (smart pointers, containers), use = default for all five.

Operator Overloading

Custom operators
class Point {
public:
    double x, y;

    Point operator+(const Point& other) const {
        return {x + other.x, y + other.y};
    }

    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }

    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        os << "(" << p.x << ", " << p.y << ")";
        return os;
    }
};

Point a{1.0, 2.0}, b{3.0, 4.0};
Point c = a + b;
std::cout << c;  // (4, 6)

friend grants the non-member function access to private members. The stream insertion operator << must be a non-member function.