当类成员来自其他类时如何实现构造函数?以Stack与Array类为例
Hey, great question! When you're building a class (like your Stack) that relies on another class (Array<T>) as a member variable, the core trick is using constructor initializer lists—they’re not just more efficient, but often mandatory if the member class doesn’t have a default constructor. Let’s break this down with your Array/Stack example, covering all common scenarios:
Array<T> template to work with Before we dive into Stack, here’s a minimal Array<T> implementation (with common constructors) so our examples make sense:
template <typename T> class Array { private: T* data; size_t size; public: // Default constructor: empty array Array() : data(nullptr), size(0) {} // Constructor with explicit size explicit Array(size_t sz) : size(sz) { data = new T[sz](); // Value-initialize elements } // Copy constructor: duplicate another array Array(const Array& other) : size(other.size) { data = new T[size]; for (size_t i = 0; i < size; ++i) { data[i] = other.data[i]; } } // Move constructor: take ownership of another array's resources Array(Array&& other) noexcept : data(other.data), size(other.size) { other.data = nullptr; other.size = 0; } // Destructor: clean up memory ~Array() { delete[] data; } // Helper: get current size size_t getSize() const { return size; } // Operator to access elements T& operator[](size_t idx) { return data[idx]; } const T& operator[](size_t idx) const { return data[idx]; } };
If you want your Stack to start with a default capacity (say, 10 elements), you’ll initialize the Array<T> member directly in the initializer list, along with your stack-top tracker:
template <typename T> class Stack { private: Array<T> arr; // Our Array member size_t topIndex; // Tracks the next empty position in the stack public: // Default constructor: create Array with capacity 10, stack starts empty Stack() : arr(10), topIndex(0) {} };
Why initializer list? This directly calls Array<T>’s Array(size_t sz) constructor for arr. If we tried to assign arr = Array<T>(10) inside the constructor body, it would first default-construct arr (wasting time/memory) then overwrite it—initializer lists avoid that overhead.
If you want users to specify the stack’s initial capacity, just pass the parameter through to the Array<T> constructor in the initializer list:
template <typename T> class Stack { private: Array<T> arr; size_t topIndex; public: // Explicit constructor: let user set initial capacity explicit Stack(size_t capacity) : arr(capacity), topIndex(0) {} };
The explicit keyword prevents accidental implicit conversions (like Stack<int> s = 5;), which is a good practice for single-argument constructors.
If you want to build a stack from a pre-existing Array<T> (either copying or moving its data), use the initializer list to call Array<T>’s copy/move constructor:
template <typename T> class Stack { private: Array<T> arr; size_t topIndex; public: // ... previous constructors ... // Copy constructor: duplicate an existing Array into the stack Stack(const Array<T>& existingArray) : arr(existingArray), topIndex(0) {} // Move constructor: take ownership of a temporary Array's resources (no copy!) Stack(Array<T>&& tempArray) noexcept : arr(std::move(tempArray)), topIndex(0) {} };
The std::move here tells the compiler to use Array<T>’s move constructor instead of copy—perfect for when you’re passing a temporary array (like Stack<int> s = Array<int>(20);).
- Always use initializer lists for member classes: If
Array<T>didn’t have a default constructor, you’d get a compile error if you tried to initializearrinside the constructor body instead of the initializer list. - Leverage the member class’s constructors: Your
Stackconstructor doesn’t need to reinvent the wheel—just pass the right arguments toArray<T>’s existing constructors. - Think about resource management: If
Array<T>handles its own memory (like our example),Stackdoesn’t need to do extra work—just letArray<T>’s destructor clean up whenStackgoes out of scope.
内容的提问来源于stack exchange,提问作者nos28

