Understanding C++ Classes, Objects, and Core Concepts

Classes in C++

Struct Evolution in C++

In C, struct defines structures containing only data. C++ extends struct to support defining classes with both data members and member functions bundled together.

Class Definition Syntax

class ClassName {
    // Member variables (attributes)
    // Member functions (methods)
};

The trailing semicolon is mandatory.

Two Definition Approaches

Inline Definition: Both declaration and implementation reside within the class body.

class Rectangle {
    int width;
    int height;
    
    int calculateArea() {
        return width * height;
    }
};

External Definition: Declaration inside the class, implementation outside.

class Rectangle {
    int width;
    int height;
public:
    int calculateArea();
};

int Rectangle::calculateArea() {
    return width * height;
}

Instantiation

Creating objects from a class template:

class Person {
    std::string name;
    int age;
};

Person student;

The Three Pillars of OOP: Encapsulation, Inheritance, and Polymorphism

Encapsulation

Encapsulation combines data and operations on thatt data into a single unit, hiding internal complexity while exposing only necessary interfaces.

Consider a smartphone: users interact through the screen and buttons, unaware of the complex circuitry within. This protective barrier prevents unauthorized access and misuse.

Implementation involves:

  1. Grouping data members and member functions together
  2. Controlling visibility through access specifiers

Access Specifeirs:

Specifier Access Level
public Accessible from anywhere
protected Accessible within class and derived classes
private Accessible only within the class

Key Rules:

  • Access scope begins at the specifier and continues until the next specifier or class end
  • class defaults to private access; struct defaults to public (for C compatibility)
  • Members of the same class can access each other's private members
class BankAccount {
private:
    double balance;
    std::string accountNumber;
    
public:
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    double getBalance() const {
        return balance;
    }
};

Class Size and Memory Layout

Objects store only member variables, not member functions. All objects of a class share the same function code.

#include <iostream>
class StudentA {
    char grade;
    int score;
    void display() {
        std::cout << "Display" << std::endl;
    }
};

class StudentB {
    bool passed;
    void display() {
        std::cout << "Display" << std::endl;
    }
};

class EmptyClass {
};

int main() {
    StudentA a;
    StudentB b;
    EmptyClass c;
    
    std::cout << sizeof(a) << std::endl;  // 8 (with padding)
    std::cout << sizeof(b) << std::endl;  // 4 (with padding)
    std::cout << sizeof(c) << std::endl;  // 1 (placeholder byte)
}

Memory Alignment: Class size equals the sum of member variables, aligned to the largest member's boundary. Empty classes receive a 1-byte placeholder to ensure unique object addresses.

The this Pointer

When multiple objects call the same member function, how does it know which object's data to manipulate?

class Counter {
    int value;
public:
    void setValue(int v) {
        this->value = v;  // Explicitly uses this pointer
    }
};

this Pointer Characteristics:

  1. Type is ClassName* const — cannot be reassigned
  2. Available only within member functions
  3. Acts as an implicit parameter passing the object's address
  4. Automatically passed by the compiler via the ecx register

The Six Special Member Functions

These functions generate automatically if not explicitly defined.

Constructor

Executes during object initialization.

class Employee {
    std::string name;
    int id;
public:
    Employee() {
        name = "Unknown";
        id = 0;
    }
    
    Employee(std::string n, int i) {
        name = n;
        id = i;
    }
};

Employee e1;              // Default constructor
Employee e2("Alice", 101); // Parameterized constructor

Constructor Properties:

  • Name matches the class name exactly
  • No return type
  • Invoked automatically during object creation
  • Supports overloading
  • If any constructor is explicitly defined, the compiler stops generating a default one

Default Constructors: Either no-parameter or all-parameters-have-defaults. Only one default constructor should exist per class.

class Product {
    std::string title;
    double price;
public:
    Product(std::string t = "", double p = 0.0) {
        title = t;
        price = p;
    }
};

Product p1;           // OK
Product p2("Widget");  // OK

Compiler-Generated Constructor Behavior: For built-in types (int, char, etc.), it does nothing. For user-defined types, it calls their constructors.

Destructor

Cleans up resources when objects go out of scope.

class DynamicArray {
    int* data;
    size_t capacity;
public:
    DynamicArray(size_t cap = 10) {
        data = new int[cap];
        capacity = cap;
    }
    
    ~DynamicArray() {
        delete[] data;
        data = nullptr;
    }
};

Destructor Properties:

  • Prefixed with ~
  • Takes no parameters, returns nothing
  • Cannot be overloaded — only one per class
  • Called automatically when object lifetime ends
  • Handles heap-allocated memory; stack memory auto-cleans

Copy Constructor

Creates a new object as a duplicate of an existing one.

class Point {
    int x;
    int y;
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {}
    
    // Copy constructor - must use reference!
    Point(const Point& p) {
        x = p.x;
        y = p.y;
    }
};

Point p1(10, 20);
Point p2(p1);  // Copy constructor called

Why Reference Parameter? Passing by value triggers another copy, causing infinite recursion.

Default Behavior: Shallow copy (byte-by-byte memory copy). Handles built-in types directly and user-defined types via their copy constructors.

Assignment Operator Overloading

class Vector2D {
    int x, y;
public:
    Vector2D(int a = 0, int b = 0) : x(a), y(b) {}
    
    Vector2D& operator=(const Vector2D& other) {
        if (this != &other) {
            x = other.x;
            y = other.y;
        }
        return *this;
    }
};

Vector2D v1(1, 2);
Vector2D v2;
v2 = v1;  // Assignment operator

Operator Overloading Syntax:

ReturnType operator+(Parameters);

Important Restrictions:

  • Cannot invent new operators
  • Must have at least one class-type operand
  • Cannot alter built-in operator semantics
  • ., ::, sizeof, ?:, and .* cannot be overloaded

Complete Date Class Example:

class Date {
    int year, month, day;
    
    int daysInMonth() {
        int mdays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
            return 29;
        return mdays[month];
    }
    
public:
    Date(int y = 0, int m = 1, int d = 1) : year(y), month(m), day(d) {}
    
    Date(const Date& other) : year(other.year), month(other.month), day(other.day) {}
    
    Date& operator=(const Date& other) {
        if (this != &other) {
            year = other.year;
            month = other.month;
            day = other.day;
        }
        return *this;
    }
    
    bool operator<(const Date& other) {
        if (year != other.year) return year < other.year;
        if (month != other.month) return month < other.month;
        return day < other.day;
    }
    
    Date& operator+=(int days) {
        day += days;
        while (day > daysInMonth()) {
            day -= daysInMonth();
            month++;
            if (month > 12) {
                month = 1;
                year++;
            }
        }
        return *this;
    }
    
    Date& operator++() {
        *this += 1;
        return *this;
    }
    
    Date operator++(int) {
        Date temp(*this);
        *this += 1;
        return temp;
    }
};

const Member Functions

const after the function signature restricts modifications to member variables.

class Circle {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double getRadius() const {
        return radius;
    }
    
    double getArea() const {
        return 3.14159 * radius * radius;
    }
};

const Circle c(5.0);
double r = c.getRadius();  // OK - const function called

Access Rules:

  • const objects can only call const member functions
  • non-const objects can call both const and non-const functions
  • const member functions cannot invoke non-const members
  • non-const members can safely call const members

When to Use const: Apply to any member functon that doesn't modify state.

Address-of Operators

Typically uses compiler-generated versions. Rarely needs custom implementation.

class Widget {
    int data;
public:
    Widget* operator&() {
        return this;
    }
    
    const Widget* operator&() const {
        return this;
    }
};

Advanced Topics

Initializer Lists

Provide direct initialization for members.

class Coordinate {
    int x, y, z;
public:
    Coordinate(int a, int b, int c) : x(a), y(b), z(c) {}
};

Required Situations:

  • const members (require initialization at definition)
  • Reference members (must bind at definition)
  • Members without default constructors
class Buffer {
    const size_t maxSize;
    int& indexRef;
public:
    Buffer(size_t size, int& idx) : maxSize(size), indexRef(idx) {}
};

Order Dependency: Initialization occurs in declaration order, not list order. Align both for clarity.

Implicit Type Conversions

Single-parameter constructors enable implicit conversions:

class Distance {
    double meters;
public:
    Distance(double m = 0) : meters(m) {}
};

void printDistance(Distance d);

printDistance(100.5);  // Implicit conversion from double

Prevent with explicit:

class Distance {
    double meters;
public:
    explicit Distance(double m = 0) : meters(m) {}
};

printDistance(100.5);  // Error - conversion disabled
printDistance(Distance(100.5));  // OK - explicit

Static Members

Shared across all instances of a class.

class Counter {
    static int instanceCount;
    int id;
public:
    Counter() {
        id = ++instanceCount;
    }
    
    static int getCount() {
        return instanceCount;
    }
};

int Counter::instanceCount = 0;

Static Variables: Declared in class, defined outside. Exist in static storage, shared by all objects.

Static Functions: No this pointer. Call via ClassName::function() syntax.

Access Rules:

  • Static functions cannot call non-static functions (no this)
  • Non-static functions can call static functions

Friend Declarations

Grant external functions or classes access to private members.

class Teacher;

class Student {
    friend class Teacher;
    friend void displayRecord(const Student&);
    
private:
    std::string name;
    int score;
    
public:
    Student(std::string n, int s) : name(n), score(s) {}
};

void displayRecord(const Student& s) {
    std::cout << s.name << ": " << s.score << std::endl;
}

class Teacher {
public:
    void viewStudent(const Student& s) {
        std::cout << "Name: " << s.name << std::endl;
        std::cout << "Score: " << s.score << std::endl;
    }
};

Cautions: Friends break encapsulation. Use sparingly and intentionally.

Tags: C++ OOP Classes Objects Constructors

Posted on Wed, 13 May 2026 16:11:19 +0000 by mY.sweeT.shadoW