move constructor and move assignment operator

Take a look at the addition operator + as implemented in demolist12_4. Notice that it actually creates a copy and returns it. While making it easy to concatenate the strings, the addition operator + can cause performance problems. But it's solved by using move constructors and move assignment operators.

declaring a move constructor and move assignment operator

The syntax of move constructor is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Smaple
{
private:
Type* ptr_resource;
public:
Sample(Sample &&MoveSource)
{
ptr_resource = MoveSource.ptr_resource;
MoveSource.ptr_resource = NULL;
}

Sample& operator = (Sample&& MoveSource)
{
if(this != &MoveSource)
{
delete [] ptr_resource;
ptr_resource=MoveSource.ptr_resource;
MoveSource.ptr_resource=NULL;
}
}
Sample();
Sample(const Sample& CopySource);
Sample& operator= (const Sample& CopySource);
};

Additionally, as the input parameter is move source, it cannot be a const parameter as it is modified.

review : copy constructor

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
MyString(const MyString &copySource) // Copy constructor
{
buffer = NULL;
cout << "Copy constructor: copying from MyString" << endl;
if (copySource.buffer != NULL)
{
// allocate own buffer
buffer = new char[strlen(copySource.buffer) + 1];

// deep copy from the source into local buffer
strcpy(buffer, copySource.buffer);

cout << "buffer points to: 0x" << hex;
cout << (unsigned int *)buffer << endl;
}
};
void UseMyString(MyString str)
{
cout << "String buffer in MyString is " << str.GetLength();
cout << " characters long" << endl;

cout << "buffer contains: " << str.GetString() << endl;
return;
}

int main()
{
MyString SayHello("hello");
UseMyString(SayHello);
}

Notice that copy constructor is called in line 21, simultaneously, proceeding copy from str to SayHello.

C++11 compliant compilers ensure that for rvalue temporaries the move constructor is used instead of the copy constructor and the move assignment operator is invoked instead of the copy assignment operator.

e.g. class with move constructor and move assignment operator in addition to copy constructor and copy assignment operator. demolist12_11

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <iostream>
#include <string.h>
using namespace std;

class MyString
{
private:
char *buffer_;
MyString() : buffer_(NULL)
{
cout << "default constructor" << endl;
}

public:
MyString(const char *initial_input) // constructor
{
cout << "constructor called " << initial_input << endl;
if (initial_input != NULL)
{
buffer_ = new char[strlen(initial_input) + 1];
strcpy(buffer_, initial_input);
}
else
buffer_ = NULL;
}
/*/////////
MyString(MyString &&MoveSource) // move constructor
{
cout << "Move constructor" << MoveSource.buffer_ << endl;
if (MoveSource.buffer_ != NULL)
{
buffer_ = MoveSource.buffer_;
MoveSource.buffer_ = NULL;
}
}
MyString &operator=(MyString &&MoveSource) // move assignment operator
{
cout << "move assignment operator move " << MoveSource.buffer_ << endl;
if ((MoveSource.buffer_ != NULL) && this != &MoveSource)
{
delete[] buffer_;
buffer_ = MoveSource.buffer_;
MoveSource.buffer_ = NULL;
}

return *this;
}
///////////*/

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

MyString &operator=(const MyString &CopySource) // Copy assignment operator
{
cout << "copy assignment operator " << CopySource.buffer_ << endl;
if ((this != &CopySource) && (CopySource.buffer_ != NULL))
{
if (buffer_ != NULL)
delete[] buffer_;
buffer_ = new char[strlen(CopySource.buffer_) + 1];
strcpy(buffer_, CopySource.buffer_);
}
return *this;
}

~MyString() // destructor
{
if (buffer_ != NULL)
delete[] buffer_;
}
int GetLength()
{
return strlen(buffer_);
}
operator const char *()
{
return buffer_;
}

MyString operator+(const MyString &AddThis)
{
cout << "operator + called " << endl;
MyString NewStr;
if (AddThis.buffer_ != NULL)
{
NewStr.buffer_ = new char[GetLength() + strlen(AddThis.buffer_) + 1];
strcpy(NewStr.buffer_, buffer_);
strcat(NewStr.buffer_, AddThis.buffer_);
}

return NewStr;
}
};

int main()
{
MyString Hello("hello ");
MyString World("world ");
MyString Cpp("C++ ");

MyString HelloAgain("overwrite this");
HelloAgain = Hello + World + Cpp;

return 0;
}

commented line 26-48 the output is:

1
2
3
4
5
6
7
8
9
constructor called hello 
constructor called world
constructor called C++
constructor called overwrite this
operator + called
default constructor
operator + called
default constructor
copy assignment operator hello world C++

whereas uncommented it:

1
2
3
4
5
6
7
8
9
constructor called hello 
constructor called world
constructor called C++
constructor called overwrite this
operator + called
default constructor
operator + called
default constructor
move assignment operator move hello world C++

The move constructor save a good amount of processing time in reducing unwanted memory allocations and copy steps. Programming the move constructor and the move assignment operator is completely optional . Unlike the copy constructor and the copy assignment operator, the compiler does not add a default implementation.

User Defined literals

c++ 11 extend the standard's support of literals by allowing you define your own literals.

To define your own literals, you define operator "" like this:

1
2
3
4
ReturnType operator "" YourLiteral(ValueType value)
{
//conversion code here
}

e.g. demonstrates a user defined literal that convert types. conversion from fahrenheit and centigrade to kelvin scale. demolist12_12

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
#include <iostream>
using namespace std;

struct Temperature
{
double kelvin_;
Temperature(long double kelvin) : kelvin_(kelvin) {}
};

Temperature operator"" _C(long double celcius)
{
return Temperature(celcius + 273);
}

Temperature operator""_F(long double fahrenheit)
{
return Temperature((fahrenheit + 459.67) * 5 / 9);
}

int main()
{
Temperature K1 = 31.73_F;
Temperature K2 = 0.0_C;
cout << "K1 is " << K1.kelvin_ << " K" << endl;
cout << "K2 is " << K2.kelvin_ << " K" << endl;

return 0;
}

output

1
2
K1 is 273 K
K2 is 273 K

Line 21 and 22 in the sample above initialize two instances of Temperature. The two literals are defined in line 9-17.

Notice that the initialization of K1 and K2 by using like:
K1 = Temperature (273)

operators cannot be overloaded

operators keep some cards to themselves by not allowing you to change or alter the behavior of some operators that are expected consistently.

The operator cannot be overloaded or redefinded

operator name
. Member selection
.* Pointer-to-member selection
:: Scope resolution
? : Conditional ternary operator
sizeof Gets the size of an object/class type