CODE: C CPP Functions, and Functions Types

Classes are used to create user-defined data types. We can use these basic data types to create our own class. The cool part is that our class can contain multiple variables, pointers, and functions which would be available to us whenever a class object is created.

This is where Object-Oriented Programming (OOP) begins.

At the heart of OOP lies the concept of the class, which allows us to create user-defined data types and model real-world or logical entities directly in code.

From Data Types to Objects

An object is an instance of a class.

  • int, bool, and string describe _what kind of data_ we store.
  • A class describes:
  • what data an object owns
  • what operations it can perform
  • how it is created
  • how it is destroyed

A class can contain:

  • variables (data members)
  • pointers and references
  • functions (member functions / methods)

Each object created from the class gets its own copy of the data but shares the same behavior definition.


Class

A class in C++ is declared using the class keyword, followed by the class name and a body enclosed in curly braces.

cpp
class MyClass {
public:
    int myNumber;        // Public attribute
    void myFunction();   // Public method declaration
};

This declaration defines a type, not an object.

No memory is allocated until an object is created. (Just the blue print)


Access Specifiers: Controlling Visibility

Encapsulation is one of the core principles of OOP.

C++ enforces encapsulation using access specifiers:

  • public
  • Accessible from anywhere using the . operator
  • private (default)
  • Accessible only inside the class
  • protected
  • Accessible inside the class and by derived classes

cpp
class Example {
private:
    int secret;
protected:
    int inheritedValue;
public:
    int visible;
};

Why this matters

Access control prevents invalid states, enforces invariants, and enables safe APIs.


Member Functions (Methods)

Functions defined inside a class are called member functions.

They describe the behavior of the object.

cpp
class MyClass {
public:
    int myNumber;

    void myFunction() {
        cout << "Hello World!" << endl;
    }
};

Member functions automatically have access to the object’s internal state.


Creating and Using Objects

Once a class is defined, we can create objects (instances) of it.

cpp
MyClass myObj;
myObj.myNumber = 15;
myObj.myFunction();

At this moment:

  • memory is allocated
  • the constructor is executed
  • the object becomes usable

Constructors: Object Construction

A constructor defines how an object is initialized.

Rules:

  • Same name as the class
  • No return type
  • Called automatically when the object is created

cpp
class MyClass {
public:
    int myNumber;

    MyClass() {
        myNumber = 10;
    }
};

Writing `void` as a return type is **not allowed** in C++ constructors.

Member Initialization List

Initialization lists initialize members before the constructor body runs.

cpp
class MyClass {
public:
    int myNumber;
    MyClass(int num) : myNumber(num) {}
};

Why use them

  • Required for const members and references
  • More efficient
  • Ensures correct initialization order

Separating Declaration and Definition (`::`)

Large systems separate interface from implementation.

cpp
class Rectangle {
    int length;
    int width;

public:
    void setLength(int l);
    int area();
};

void Rectangle::setLength(int l) {
    length = l;
}

int Rectangle::area() {
    return length * width;
}

The scope resolution operator :: binds the function definition to the class.


Types of Constructors

To use the different types of constructors

cpp
// Default constructor
Example exmaple;
// Parameterized constructor
Example example1(1,2);
// Copy constructor
Example example2 = example1;
// Moce constructor
Example example3 = std::move(example1);
// Constructor delegation
Example example4(1);
// Exceplicit constructor
// this will work with implicit constructor, and it will not work with explicit
Example example5 = 1;
// only this will work
Example example6(1,2);

Default Constructor

cpp
class Example {
public:
    Example() {}
};

If not defined, the compiler may generate one—but members are not initialized unless explicitly done.


Parameterized Constructor

cpp
class Example {
public:
    Example(int x, int y) {}
};

Used when object creation requires initial data.


Copy Constructor

Creates a new object from an existing one.

cpp
class Example {
public:
    Example(const Example& obj) {}
};

Triggered when:

  • Passing by value
  • Returning by value
  • Explicit copy

Move Constructor

Transfers ownership from a temporary object.

cpp
class Example {
public:
    Example(int x) : Example(x, 0) {}
    Example(int x, int y) {}
};

Why

  • Avoids deep copies
  • Enables high-performance code

Constructor Delegation

One constructor calls another.

cpp
class Example {
public:
    Example(int x) : Example(x, 0) {}
    Example(int x, int y) {}
};

Reduces duplication and enforces consistency.


Explicit Constructor

Prevents implicit conversions.

cpp
class Example {
public:
    explicit Example(int x) {}
};

cpp
Example e1 = 1;   // not allowed
Example e2(1);    // allowed


The `this` Pointer

this points to the current object.

cpp
class Date {
    int day, month, year;
public:
    Date(int day, int month, int year) {
        this->day = day;
        this->month = month;
        this->year = year;
    }
};

Used to:

  • resolve name conflicts
  • return the object itself
  • chain calls

Destructor: Object Destruction

The destructor is called when an object:

  • goes out of scope
  • is deleted

cpp
class Example {
public:
    ~Example() {
        // cleanup
    }
};

Every class that manages resources must define a destructor.


`default` and `delete`

`= default`

Normally the compiler will generate the constructor and the destructor automatically if it is not defined by the user, but you can control this behaviour by using default and delete keywords.

  • The default keyword is used when you want the compiler to generate a default implementation of a constructor (or other special member functions like the copy constructor, move constructor, copy assignment operator, or move assignment operator).

cpp
#include <iostream>

class Example {
public:
    int x;
    // Default constructor using 'default'
    Example() = default;
    // Parameterized constructor
    Example(int value) : x(value) {}
    // Default copy constructor using 'default'
    Example(const Example&) = default;
};

int main() {
	// Calls the default constructor
    Example e1;
    // Calls the parameterized constructor
    Example e2(10);
    // Calls the default copy constructor
    Example e3 = e2;
    std::cout << "e1.x = " << e1.x << "\n";  // Undefined value (not initialized)
    std::cout << "e2.x = " << e2.x << "\n";  // Output: e2.x = 10
    std::cout << "e3.x = " << e3.x << "\n";  // Output: e3.x = 10
    return 0;
}

Tells the compiler to generate the function.


`= delete`

The delete keyword is used when you want to prevent the compiler from generating a default implementation of a constructor (or other special member functions). It essentially disables the use of that function, preventing objects from being created or copied in a certain way.

cpp
#include <iostream>
class Example {
public:
    int x;
    // Default constructor
    Example() : x(0) {}
    // Parameterized constructor
    Example(int value) : x(value) {}
    // Delete the copy constructor
    Example(const Example&) = delete;
    // Delete the assignment operator
    Example& operator=(const Example&) = delete;
};

int main() {
	// Calls the default constructor
    Example e1;
    // Calls the parameterized constructor
    Example e2(10);
    // Error: Copy constructor is deleted
    // Example e3 = e2;

	// Error: Assignment operator is deleted
    // e1 = e2;

    std::cout << "e1.x = " << e1.x << "\n";  // Output: e1.x = 0
    std::cout << "e2.x = " << e2.x << "\n";  // Output: e2.x = 10
    return 0;
}

Prevents copying or assignment.

The behavior of the compiler varies based on what special members the user has defined. We can find details in the diagram by Howard Hinnant below:

Default
Constructor
DestructorCopy
Constructor
Copy
Assignment
Move
Constructor
Move
Assignment
User Declares Nothingdefaultdefaultdefaultdefaultdefaultdefault
Any ConstructorNot declareddefaultdefaultdefaultdefaultdefault
Default
Constructor
User
declared
defaultdefaultdefaultdefaultdefault
DestructordefaultUser declareddefaultdefaultNot declaredNot declared
Copy
Constructor
Not declareddefaultUser
declared
defaultNot declaredNot declared
Copy
Assignment
defaultdefaultdefaultUser declaredNot declaredNot declared
Move
Constructor
Not declareddefaultdeleteddeletedUser declaredNot declared
Move
Assignment
defaultdefaultdeleteddeletedNot declared

Special Member Function Generation Rules

SituationCompiler Behavior
No user-declared membersAll generated
Custom destructorMove operations deleted
Custom copy constructorMove operations deleted
Custom move constructorCopy operations deleted

(Howard Hinnant’s rules apply here exactly as in your table.)

Rule of 0 / 3 / 5 (Core of Modern C++)

Rule of 0 (Preferred)

If your class does **not** manage resources, do not write any special member functions.

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

Let the compiler do everything.


Rule of 3 (Legacy / Manual Ownership)

If you manage a resource manually, you must define:

  1. Destructor
  2. Copy constructor
  3. Copy assignment operator

cpp
class Buffer {
    int* data;
public:
    ~Buffer();
    Buffer(const Buffer&);
    Buffer& operator=(const Buffer&);
};


Rule of 5 (Modern C++)

Add move semantics:

  1. Move constructor
  2. Move assignment operator

cpp
class Buffer {
public:
    ~Buffer();
    Buffer(const Buffer&);
    Buffer& operator=(const Buffer&);
    Buffer(Buffer&&);
    Buffer& operator=(Buffer&&);
};


Why This Rule Exists

Because ownership must be unambiguous.

If you don’t explicitly define behavior, the compiler will—sometimes incorrectly for your intent.