Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Core Concepts of Constructors, Destructors, and Operator Overloading in C++ Classes

Tech May 12 2

Default Member Functions

Default member functions are those implicitly generated by the compiler when a class does not explicitly define them. For an empty class, the compiler creates six default members, with the first four being essential: constructor, destructor, copy constructor, and assignment operator. Two additional defaults—move constructor and move assignment—are added from C++11 onward.

Two perspectives help in mastering these:

  • Understand the compiler-generated behavior for unimplemented cases.
  • Know how to implement custom versions when default behavior is inadequate.

Constructor

Constructors initialize objects at instantiation rather than allocate memory. They replace manual Init methods used in older designs. Characteristics:

  • Name matches the class name.
  • No return type, not even void.
  • Invoked automatically during object creation.
  • Can be overloaded.
  • If no constructor is defined, a compiler-generated parameterless one is created; defining any constructor suppresses this automatic generation.
  • Parameterless, default-parameter, and compiler-generated constructors are all considered default constructors. Only one may exist to avoid ambiguity.
  • Compiler-generated constructors leave built-in-type members uninitialized (behavior compiler-dependent), but call the default constructor of each member of class type. If such a member lacks a default constructor, initialization fails unless an initializer list is used.
#include <iostream>
#include <cstdlib>
using namespace std;

typedef int DataType;

class Buffer {
public:
    Buffer(size_t sz = 8) {
        ptr = static_cast<DataType*>(malloc(sizeof(DataType) * sz));
        if (!ptr) {
            perror("malloc failed");
            return;
        }
        cap = sz;
        len = 0;
    }
private:
    DataType* ptr;
    size_t cap;
    size_t len;
};

class QueueFromBuffers {
    Buffer enqueueBuf;
    Buffer dequeueBuf;
};

int main() {
    QueueFromBuffers qb;   // compiler calls Buffer() for both members
}

Example with explicit constructors:

#include <iostream>
using namespace std;

class Calendar {
public:
    Calendar() : yr(1), mo(1), dy(1) {
        cout << "no-arg ctor\n";
    }
    Calendar(int y, int m, int d) : yr(y), mo(m), dy(d) {
        cout << "arg ctor\n";
    }
    void display() const {
        cout << yr << "/" << mo << "/" << dy << "\n";
    }
private:
    int yr, mo, dy;
};

int main() {
    Calendar c1;              // uses no-arg ctor
    Calendar c2(2025, 1, 1); // uses arg ctor
}

Destructor

Destructors clean up resources when an object’s lifetime ends; they do not destroy the storage itself (stack cleanup is automatic). They correspond to manual Destroy patterns. Characteristics:

  • Name is ~ClassName.
  • No parameters, no return type.
  • At most one destructor per class; compiler supplies one if absent.
  • Called automatically at end of scope.
  • Compiler-generated version ignores built-in types but invokes destructors of class-type members.
  • Explicit destructor still ensures class-type members are properly destroyed.

Copy Constructor

A copy constructor initializes a new object using an existing object of the same class. Its first parameter is a reference to the class type; extra parameters have defaults.

Characteristics:

  • It is a special constructor, overload of the normal constructor.
  • First argument must be a reference; passing by value causes infinite recursion.
  • Copying a class-type object by value (parameter or return) triggers the copy constructor.
  • Absent user definition, compiler generates one performing bitwise (shallow) copy for built-ins and invoking member copy constructors for class types.
  • For classes managing resources (e.g., dynamic memory), shallow copy is unsafe; deep copy must be implemented manually.
  • If a class defines a destructor releasing resources, it likely needs a custom copy constructor.
  • Return-by-value creates a temporary via copy construction; returning a reference avoids this if lifetime permits.

Assignment Operator Overloading

Assignment assigns values between two existing objects (distinct from copy construction which creates a new object).

Operator Overloading Basics

Operators can be redefined for class types. An overloaded operator is a function named operatorX where X is the symbol. Rules:

  • Number of parameters equals number of operands (one for unary, two for binary).
  • As a member functon, the left operand is this; parameter count is one less.
  • Precedence and associativity match the built-in version.
  • Cannot create new operators; five operators (::, .*, sizeof, ?:, .) cannot be overloaded.
  • At least one parameter must be class type; cannot alter meaning for built-ins.

Example of pointer-to-member usage:

#include <iostream>
using namespace std;

class Demo {
public:
    void show() { cout << "Demo::show()\n"; }
};

typedef void(Demo::*MemFunc)();

int main() {
    Demo obj;
    MemFunc f = &Demo::show;
    (obj.*f)();
}

Assignment Operator Implementation

Rules:

  • Must be a member function.
  • Prefer const ClassName& parameter to avoid unnecessary copying.
  • Return *this by reference for chaining.
  • Compiler-generated version copies built-ins bitwise and calls assignment operators of class-type members.
  • Implement manually for resource-managing classes.
  • Presence of a user-defined destructor often implies need for custom assignment.

Calendar Class Example

Header:

#pragma once
#include <iostream>
#include <cassert>

class Calendar {
    friend std::ostream& operator<<(std::ostream&, const Calendar&);
    friend std::istream& operator>>(std::istream&, Calendar&);
public:
    Calendar(int y, int m, int d);
    Calendar(const Calendar& src)
        : yr(src.yr), mo(src.mo), dy(src.dy) {}

    int daysInMon(int y, int m) const;
    void output() const;
    bool isValid() const;

    bool lt(const Calendar& other) const;
    bool lte(const Calendar& other) const;
    bool gt(const Calendar& other) const;
    bool gte(const Calendar& other) const;
    bool eq(const Calendar& other) const;
    bool neq(const Calendar& other) const;

    Calendar& incDays(int n);
    Calendar plusDays(int n) const;
    Calendar& decDays(int n);
    Calendar minusDays(int n) const;
    int diff(const Calendar& other) const;

    Calendar& preInc();
    Calendar postInc();
    Calendar& preDec();
    Calendar postDec();
private:
    int yr, mo, dy;
};

Key implementation ideas (excerpt):

  • isValid() checks range constraints.
  • Comparison operators use lexicographic year/month/day evaluation.
  • Arithmetic operators adjust date fields hendling month/year roll-over.
  • Increment/decrement operators distinguish pre/post via dummy int parameter for postfix.
  • Binary - computes day difference via iterative approach.
  • Stream operators handle formatted I/O with validation.

Efficiency note: arithmetic operators like minusDays delegate to compound assignment (decDays) to minimize coppy constructions.

Address-of Operator Overloading

Const Member Functions

Adding const after the parameter list makes a member function read-only by binding this to const ClassType* const. This prevents modification of member data.

Overloading &

Two forms: one for ordinary objects, one for const objects. Usually compiler-generated versions suffice. Custom versions can restrict address exposure.

class Calendar {
public:
    Calendar* addr() { return this; }           // non-const
    const Calendar* addr() const { return this; } // const
private:
    int yr, mo, dy;
};

The appropriate version is selected based on object’s const-ness.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.