Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Object-Oriented Programming

Classes and Encapsulation

Classes: User-Defined Types

Classes are the foundation of object-oriented programming in C++. They combine data (members) and behavior (methods) into cohesive types with controlled access.

class Point {
private:
    double x_, y_;  // Data members (by convention, trailing underscore)
    
public:
    Point(double x, double y) : x_(x), y_(y) {}  // Constructor
    
    double distance() const {  // Const member function
        return std::sqrt(x_ * x_ + y_ * y_);
    }
    
    void setX(double x) { x_ = x; }  // Mutator
    double getX() const { return x_; }  // Accessor
};

Access Specifiers

C++ provides three access levels:

  • private: Accessible only within the class
  • protected: Accessible within the class and derived classes
  • public: Accessible everywhere

Default access: class defaults to private, struct defaults to public.

class MyClass {
    int private_by_default;
public:
    int explicitly_public;
};

struct MyStruct {
    int public_by_default;
private:
    int explicitly_private;
};

Convention: Use class for types with invariants requiring encapsulation; use struct for passive data containers (POD types).

Constructors and Initialization

Constructors initialize objects. C++ provides several kinds:

Default Constructor

class Widget {
public:
    Widget() : value_(0) {}  // Default constructor
private:
    int value_;
};

Widget w;  // Calls default constructor

Parameterized Constructor

class Point {
public:
    Point(double x, double y) : x_(x), y_(y) {}
private:
    double x_, y_;
};

Point p(3.0, 4.0);  // Direct initialization
Point q = {5.0, 6.0};  // Aggregate initialization (C++11)

Member Initializer Lists

Always prefer initializer lists over assignment in constructor body:

class Container {
    std::string name_;
    std::vector<int> data_;
public:
    // Good: Direct initialization
    Container(std::string n) : name_(n), data_(100) {}
    
    // Bad: Default construction then assignment
    // Container(std::string n) { name_ = n; data_.resize(100); }
};

Reasons:

  1. Initialization is generally more efficient than assignment
  2. Const and reference members must use initializer lists
  3. Base classes and members without default constructors require it

Delegating Constructors (C++11)

Constructors can delegate to other constructors:

class Rectangle {
    double width_, height_;
public:
    Rectangle(double w, double h) : width_(w), height_(h) {}
    Rectangle() : Rectangle(0, 0) {}  // Delegates to above
};

Destructors and RAII

Destructors execute when objects are destroyed (scope exit, delete, exception). They’re the foundation of RAII (Resource Acquisition Is Initialization): tying resource lifetime to object lifetime.

class FileHandle {
    FILE* file_;
public:
    FileHandle(const char* path) : file_(fopen(path, "r")) {
        if (!file_) throw std::runtime_error("Failed to open");
    }
    
    ~FileHandle() {
        if (file_) fclose(file_);  // Always cleanup
    }
    
    // Prevent copying (for now)
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
};

void process() {
    FileHandle f("data.txt");
    // Use file...
}  // Destructor automatically closes file, even if exceptions thrown

Special Member Functions

The compiler auto-generates certain functions if not user-declared:

  1. Default constructor (if no constructors declared)
  2. Destructor
  3. Copy constructor: T(const T&)
  4. Copy assignment: T& operator=(const T&)
  5. Move constructor: T(T&&) (C++11)
  6. Move assignment: T& operator=(T&&) (C++11)

The Rule of Zero

Best Practice: If you can, don’t declare any of the special members. Let the compiler generate them. Use standard library types that manage resources correctly.

class Good {
    std::string name_;
    std::vector<int> data_;
    // Compiler-generated special members are correct
};

The Rule of Three/Five

If you declare any of destructor, copy constructor, or copy assignment, you should probably declare all three (Rule of Three). In modern C++, also declare move constructor and move assignment (Rule of Five).

class Buffer {
    char* data_;
    size_t size_;
public:
    // Constructor
    Buffer(size_t s) : data_(new char[s]), size_(s) {}
    
    // Destructor
    ~Buffer() { delete[] data_; }
    
    // Copy constructor
    Buffer(const Buffer& other) : data_(new char[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + size_, data_);
    }
    
    // Copy assignment
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = new char[size_];
            std::copy(other.data_, other.data_ + size_, data_);
        }
        return *this;
    }
    
    // Move constructor
    Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }
    
    // Move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }
};

However, prefer std::vector<char> and Rule of Zero!

Conceptual Check

Why prefer member initializer lists over assignment in constructor bodies?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <string>
3 
4class Person {
5 std::string name_;
6 int age_;
7public:
8 Person(std::string n, int a) : name_(std::move(n)), age_(a) {}
9
10 void display() const {
11 std::cout << name_ << " is " << age_ << " years old\n";
12 }
13};
14 
15int main() {
16 Person p("Alice", 30);
17 p.display();
18 return 0;
19}
System Console

Waiting for signal...