0%
November 23, 2022

Copy Constructor

C++

Our String Class

1#include <iostream>
2
3using std::cout;
4using std::cin;
5using std::endl;
6using std::ostream;
7
8class String {
9private:
10	size_t m_size;
11	char* m_buffer;
12
13public:
14	String(const char* string) {
15		m_size = strlen(string);
16		m_buffer = new char[m_size + 1];
17		memcpy(m_buffer, string, m_size);
18		m_buffer[m_size] = 0;
19	}
20	~String() {
21		delete[] m_buffer;
22	}
23	String(const String& other)
24		: m_size(other.m_size)
25	{
26		m_buffer = new char[m_size + 1];
27		memcpy(m_buffer, other.m_buffer, m_size);
28		m_buffer[m_size] = 0;
29	}
30
31	char& operator[](int index) {
32		return m_buffer[index];
33	}
34
35	friend ostream& operator<<(ostream& stream, const String& string);
36};

We also define our own << for printing our String object.

ostream& operator<<(ostream& stream, const String& string) {
	stream << string.m_buffer;
	return stream;
}

Why Copy Constructor is Always of this Signature? The Implicit Conversion

The function started at line 23, String(const String& other), is called the copy constructor of String. In fact, when we do the assignment:

String name("James");
String name2 = name;

The second line undergoes the following processes:

  1. Compiler will determine whether name can be fed into one of the overloadings of our constructors.
  2. If yes, it will do an implicit conversion and fed that parameter into that constructor, which is the copy constructor in our case.

Btw, anytime we see = we are always copying something unless we are doing auto& a = b, i.e., creating an alias.

Note that even a function returns a reference T& some_function() doesn't mean auto a = some_function() will store it as a reference, it keeps copying everything and create a in the copy constructor.

Problem Without our own Copy Constructor

  • By default the compiler will copy all the member variables, including the pointer m_buffer without copying the heap-allocated array. That's what we call shallow copy.
  • Exception will be caught when both name and name2 are our of the scope they live since ~String() will be called twice but both name.m_buffer and name2.m_buffer point to the same heap-allocated array.

Test our String Class in main()

int main() {
	{
		String name("James");
		cout << name << endl;
		String name2 = name;
		name2[3] = 's';
		cout << name2 << endl;
	}
	cin.get();
}