Week 9: Diving Deeper into Classes

Explore constructors, destructors, the `this` pointer, and static members in C++ classes.

Explore Chapter 9

Chapter 9: Classes In-Depth

Constructors: Initializing Objects.

A constructor is a special member function of a class that is automatically called when an object of that class is created (instantiated). Its primary purpose is to initialize the object's member variables.

Key characteristics of constructors:

  • They have the same name as the class itself.
  • They have no return type (not even `void`).
  • They can be overloaded (you can have multiple constructors with different parameter lists).

Default Constructor

A constructor that takes no arguments is called the default constructor. If you don't define any constructors yourself, the compiler will implicitly generate a default constructor that does nothing.

class MyClass {
public:
    MyClass() { // Default constructor definition
        std::cout << "Default constructor called!" << std::endl;
    }
};

int main() {
    MyClass obj1; // Calls the default constructor
    return 0;
}

Parameterized Constructor

Constructors can accept parameters to initialize member variables with specific values provided during object creation.

class Rectangle {
private:
    double width;
    double height;
public:
    // Parameterized constructor
    Rectangle(double w, double h) {
        width = w;
        height = h;
        std::cout << "Parameterized constructor called." << std::endl;
    }
    double getArea() { return width * height; }
};

int main() {
    Rectangle rect1(10.0, 5.0); // Calls parameterized constructor
    std::cout << "Area: " << rect1.getArea() << std::endl; // Output: Area: 50
    return 0;
}

If you define any constructor (like a parameterized one), the compiler will not automatically generate a default constructor.

Copy Constructor

A copy constructor creates an object by initializing it with an object of the same class, which has been created previously. The compiler provides an implicit copy constructor if you don't define one, but sometimes you need a custom one (especially when dealing with dynamic memory).

// Simplified example
Rectangle rect2 = rect1; // Calls the copy constructor (likely the implicit one here)
Rectangle rect3(rect1);  // Also calls the copy constructor

Destructors: Cleaning Up Objects.

A destructor is another special member function that is automatically called when an object of the class goes out of scope or is explicitly deleted (if created with `new`). Its primary purpose is to release resources that the object may have acquired during its lifetime (e.g., deallocate dynamic memory, close files).

Key characteristics of destructors:

  • They have the same name as the class, preceded by a tilde (`~`).
  • They have no return type and take no parameters.
  • A class can only have one destructor.
#include <iostream>
#include <string>

class Message {
private:
    std::string text;
public:
    // Constructor
    Message(std::string t) {
        text = t;
        std::cout << "Message object created: " << text << std::endl;
    }
    // Destructor
    ~Message() {
        std::cout << "Message object destroyed: " << text << std::endl;
    }
};

int main() {
    std::cout << "Entering main..." << std::endl;
    Message msg1("First");
    { // Inner scope
        Message msg2("Second");
        std::cout << "Inside inner scope..." << std::endl;
    } // msg2 goes out of scope here, destructor called
    std::cout << "Exiting main..." << std::endl;
    return 0; // msg1 goes out of scope here, destructor called
}

Output Order:

Entering main...
Message object created: First
Message object created: Second
Inside inner scope...
Message object destroyed: Second
Exiting main...
Message object destroyed: First

Destructors are crucial for preventing resource leaks, especially when working with dynamically allocated memory.

The `this` Pointer.

Inside a non-static member function of a class, the keyword `this` acts as a pointer that holds the memory address of the current object for which the method was called.

It's implicitly available within member functions and is primarily used to:

  • Distinguish between member variables and parameters with the same name.
  • Return a reference or pointer to the current object from a member function.

Example: Disambiguation

class Example {
private:
    int value;
public:
    // Parameter name 'value' is the same as the member variable name
    void setValue(int value) {
        // 'this->value' refers to the member variable
        // 'value' refers to the parameter
        this->value = value;
    }
    int getValue() { return value; } // 'this->' is implicit here
};

int main() {
    Example obj;
    obj.setValue(42);
    std::cout << "Value: " << obj.getValue() << std::endl; // Output: 42
    return 0;
}

While you often don't need to explicitly write `this->` to access members (the compiler understands it implicitly), it's necessary when there's a naming conflict like in the `setValue` example.

`static` Members (Variables and Methods).

Class members (variables and methods) can be declared as `static`. Static members belong to the class itself, rather than to any specific object (instance) of the class.

Static Member Variables

  • There is only one copy of a static member variable shared among all objects of the class.
  • They must be defined (usually initialized) outside the class definition, typically in the `.cpp` file.
  • They are accessed using the class name and the scope resolution operator (`::`), e.g., `ClassName::staticVariable`.
// --- Thing.h ---
class Thing {
public:
    static int objectCount; // Declaration
    Thing() { objectCount++; } // Increment count when an object is created
};

// --- Thing.cpp ---
// #include "Thing.h" // Assuming separate files
int Thing::objectCount = 0; // Definition and initialization

// --- main.cpp ---
#include <iostream>
// #include "Thing.h" // Assuming separate files

int main() {
    std::cout << "Initial count: " << Thing::objectCount << std::endl; // Output: 0
    Thing t1;
    Thing t2;
    std::cout << "Count after creating objects: " << Thing::objectCount << std::endl; // Output: 2
    return 0;
}

Static Member Functions (Methods)

  • Static methods also belong to the class, not a specific object.
  • They can be called using the class name and scope resolution operator (`ClassName::staticMethod()`) without needing an object instance.
  • They can only access static member variables and other static member functions directly. They do not have access to the `this` pointer or non-static members.
class MathUtils {
public:
    static double PI; // Static variable
    static double circleArea(double radius) { // Static method
        return PI * radius * radius; // Accesses static member PI
    }
};

// Definition outside class
double MathUtils::PI = 3.14159;

int main() {
    double area = MathUtils::circleArea(10.0); // Call static method using class name
    std::cout << "Area: " << area << std::endl; // Output: Approx 314.159
    return 0;
}

Static members are useful for defining constants shared by all objects or utility functions related to the class that don't depend on a specific object's state.

Syllabus