a C++ Copy Constructor for ch_stack (Deep Copy from Prototype)

Dynamic Stack  «Prev  Next»
Lesson 5 Defining a copy constructor
Objective Add a constructor to the ch_stack class based on a supplied function prototype.

Defining a Copy Constructor for ch_stack

A copy constructor is essential for the ch_stack class because its default memberwise copy would result in a shallow copy, causing multiple objects to share the same dynamically allocated buffer and leading to dangerous dangling pointers or double-deletion errors upon destruction. By implementing an explicit copy constructor, we ensure a deep copy where a new independent buffer is allocated and only the logically active elements (determined by the top index) are duplicated, preserving both the stack's capacity and its current content correctly. Modern C++ best practices recommend using std::copy_n instead of memcpy to make the number of elements being copied explicit and safer. This implementation also highlights the importance of the Rule of Three/Five, as manual memory management through raw pointers requires a matching destructor and copy-assignment operator (and ideally move semantics) to maintain resource safety. While this exercise teaches valuable lessons about ownership and resource management, production code would typically avoid these complexities altogether by using std::vector to achieve correct, automatic deep copying behavior.

Why ch_stack needs an explicit copy constructor

In C++, a copy constructor is the constructor used to create a new object from an existing object of the same type. If you don’t write one, the compiler generates a default copy constructor whose behavior is memberwise copy.

Memberwise copy is often correct for value-type members (like integers), but it is dangerous when your class owns a raw pointer. If ch_stack stores its elements in dynamically allocated memory (for example, char* s), then memberwise copy duplicates only the pointer value. That creates shallow copy semantics: two objects end up pointing at the same buffer. When one object destroys the buffer, the other object is left with a dangling pointer—leading to use-after-free bugs and double deletes.

The supplied prototype: what you’re implementing

The compiler’s default copy-constructor signature for ch_stack is:

ch_stack::ch_stack(const ch_stack&);

Your job in this lesson is to provide an explicit implementation that performs a deep copy: allocate a new internal buffer and copy the source object’s data into it.

Design decision: copy capacity or copy live elements?

A typical stack implementation has:

When you copy a stack, you usually copy:

Copy constructor for ch_stack (deep copy)

Below is a modernized version of the copy constructor that still matches the classic “manual memory” learning goal, but avoids memcpy and makes the copy length explicit. This assumes top represents the index of the top element (with -1 meaning empty), which is consistent with your earlier material.

#include <algorithm>  // std::copy_n
#include <cstddef>

ch_stack::ch_stack(const ch_stack& other)
    : max_len(other.max_len),
      top(other.top),
      s(nullptr)
{
    s = new char[max_len];

    // Copy only the elements that are logically present on the stack.
    // If top == -1, the stack is empty and there is nothing to copy.
    const int count = (top >= 0) ? (top + 1) : 0;
    std::copy_n(other.s, count, s);
}

What this implementation guarantees

Modern C++ note: Rule of Three/Five/Zero

If ch_stack owns dynamic memory via new[], then a correct class design also requires: a destructor, a copy-assignment operator, and usually move operations (Rule of Five). This lesson focuses on the copy constructor, but keep in mind you’re stepping into “ownership semantics,” and the rest must be consistent.

In production C++, the simplest way to avoid these pitfalls is to store the elements in std::vector<char> or std::string. Then you get correct copying “for free” (Rule of Zero), because the standard library handles ownership and copying safely.

Copy constructors in action

Copy constructors run whenever a new object is created as a copy of an existing object, such as:

ch_stack a(10);
// ... push some chars into a ...

ch_stack b(a);      // copy construction
ch_stack c = a;     // copy construction (same thing, different syntax)

Copy construction can also occur when passing an object by value into a function. In modern C++, passing by const& is usually preferred for non-trivial objects to avoid unnecessary copies.

Adding Constructor to Stack - Exercise

Click the Exercise link below to add a constructor to the ch_stack class based on a supplied function prototype.
Adding Constructor to Stack - Exercise
SEMrush Software