the difference of & used in reference and store address

A reference & is a alias of variable and must be initialized before we use it, eg:

1
2
3
4
int a=6;//initialization 
int &b=a;
cout << b <<endl;
// output is 6
Whereas, when & is used to store address, it should be used in conjunction with a pointer, eg:
1
2
3
int *x;
x = &a;
//or int *x = &a
apparently, a pointer can be used in non-initialization, in that case, it point to NULL as default.

tips: in the initialization of a pointer, int * can be regarded as a pointer type simply.

copy constructor

copy constructor is typically used for: 1. initializing object by using same type object 2. copying object and send it to function as parameter 3. copying object and return the object from function Typical format of copy constructor is:

1
2
3
4
classname(const classname &obj)
{
//the body of constructor
}

deep copy using a copy constructor

define a copy constructor and ensure deep copy of dynamically allocated buffers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <string.h>
using namespace std;
class MyString
{
private:
char *buffer;

public:
MyString(const char *initString)
{
buffer = NULL;
cout << "Default constructor: creating new MyString" << endl;
if (initString != NULL)
{
buffer = new char[strlen(initString) + 1];
strcpy(buffer, initString);
cout << "buffer point to " << hex << (int *)buffer << endl;
}
}

MyString(const MyString &copySource)
{
buffer = NULL;
if (copySource.buffer != NULL)
{
buffer = new char[strlen(copySource.buffer) + 1];
strcpy(buffer, copySource.buffer);
cout << "copy constructor buffer: " << buffer << endl;
}
}

~MyString()
{
cout << "Invoking destructor" << endl;
delete[] buffer;
}

int GetLength()
{
return (strlen(buffer));
}

const char *GetString()
{
return buffer;
}
};

void UseMyString(MyString str)
{
cout << "buffer length is " << str.GetLength() << endl;
cout << "buffer is " << str.GetString() << endl;
}
E
int main()
{
MyString SayHello("Hello ");
UseMyString(SayHello);
}

code analysis

To start with, let’s focus on main() that (as before) creates an object sayHello in Line 58 MyString SayHello("Hello ");. Creating sayHello results in the first line of output that comes from the constructor of MyString, at Line 12. For sake of convenience, the constructor also displays the memory address that buffer points to. main() then passes sayHello by value to function UseMyString() in Line 59 UseMyString(SayHello);, which automatically results in the copy constructor being invoked as shown in the output. The code in the copy constructor is similar to that in the constructor. The basic idea is the same, check the length of C-style string buffer contained in the copy source at Line 27 buffer = new char[strlen(copySource.buffer) + 1];, allocate proportional memory in one’s own instance of buffer, and then use strcpy to copy from source to destination at Line 28 strcpy(buffer, copySource.buffer);. This is not a shallow copy of pointer values. This is a deep copy where the content being pointed to is copied to a newly allocated buffer that belongs to this object.

the memory address being pointed to by buffer is different in the copy—that is, two objects don’t point to the same dynamically allocated memory address. As a result, when function UseMyString() returns and parameter str is destroyed, the destructor code does a delete[]on the memory address that was allocated in the copy constructor and belongs to this object. In doing so, it does not touch memory that is being pointed to by sayHello in main(). So, both functions end and their respective objects are destroyed successfully and peacefully without the application crashing.