04 - DynamicMemoryRuleOfThree
04 - DynamicMemoryRuleOfThree
Introduction to C++
2
++C
operator =
struct Complex{
public:
Complex(double real, double img) :
_real(real),_img(img){…}
Complex() : Complex(0,0) {…}
private:
double _real;
double _img; int main()
}; {
Complex a(1, 0), b;
b = a;
}
3
++C
investigate how operator= works on
primitives
int a = 0, b = 2, c = 5;
a = b = c;
cout << b << a << endl;
We want to allow
b = 5, a = 5 concatenation!
Therefor operator=
int a = 0, b = 2, c = 5; should return
(a = b) = c; reference!
cout << b << a << endl;
b = 2, a = 5
++C
operator=
For built-in types, the language behaves as if the
following functions exist:
int& int::operator=(const int &);
double& double::operator=(const double &);
6
++C
Default operator=
The default operator= copies all data members of the class,
by applying = on each of them (member wise copy)
In our case, the result is:
7
++C
Copy Constructor
This constructor is also called when initializing
a new object
Complex b = a;
is equivalent to writing
Complex b(a);
In both cases the compiler calls copy
constructor
8
++C
Complex a(1,3);
35%
(26)
Complex copy = returnCopy(a);
return 0; 4% (3)
A B C
74
11 }
++C
Complex returnCopy(const Complex& c)
{
Complex a = c; // Call copy constructor
return a;
}
int main()
{
Complex a(1,3);
Complex copy = returnCopy(a); // 2 copy
constructor calls
return 0;
}
12
++C
Default Copy Constructor
Same as with operator=
If not specified, default instantiation is done by
member wise copy
Constructing each member with the value of the
matching field in source
In our example:
Complex::Complex(const Complex &c)
: _real(c._real), _img(c._img)
{ }
13
++C
When is the copy c-tor executed?
14
++C
Class summary
public/private: members and functions access
Constructors: allocate resources, initialize
• default, copy
• delegation
• initializer list
Destructors: free resources
Keywords:
• static - variable or function
• const - variable, function argument, return value or
function
• mutable - class variable that can change by const
15
function
++C
16
++C
C Interface
struct IntList;
typedef struct IntList IntList;
IntList* intListNew();
void intListFree( IntList* List );
void intListPushFront( IntList* List, int x);
void intListPushBack( IntList* List, int x);
int intListPopFront( IntList* List );
int intListPopBack( IntList* List );
int intListIsEmpty( IntList const* List);
17
++C
C++ Class
In header file:
class IntList
{
public:
IntList();
~IntList();
void pushFront(int x);
void pushBack(int x);
int popFront();
int popBack();
bool isEmpty() const;
...
18 }
++C
Classes & Memory allocation
Consider this C++ code Compare to C style:
main() main()
{ {
IntList *L = IntList* L =
new IntList; (IntList*)malloc
(sizeof(IntList));
delete L; free(L);
} }
free(L);
Does not call destructor!
Internal data members are not freed
20
++C
new & delete
Special operators:
delete L;
4. Call destructor (~IntList())
5. Free memory
21
++C
new
Can be used with any type:
int *i = new int;
char **p = new (char *);
• new is a global operator
• new expression invokes the operator new to
allocate memory, and then calls constructor
• Can be overloaded (for class specific behavior), or
replaced (hide, for global behavior)
• By default, failure throws exception. Can be
changed.
• See <new> header
22
++C
new expression with exception
// allocates memory by calling:
// operator new(sizeof(MyClass))
// and then constructs an object at the
// newly allocated space
MyClass *p1 = new MyClass;
23
++C
new expression without exception
// allocates memory by calling:
// operator new(sizeof(MyClass),std::nothrow)
// and then constructs an object at the
// newly allocated space
MyClass *p2 = new (std::nothrow) MyClass;
null-pointer
if no memory was allocated
25
++C
operator new
For more information read:
http://www.cplusplus.com/reference/new/operator%20new/
http://www.cplusplus.com/reference/new/operator%20new[]/
http://en.cppreference.com/w/cpp/language/new
and...
https://isocpp.org/wiki/faq/dtors#placement-new
26
++C
New & Constructors
class MyClass
{
public:
1
MyClass();
2
MyClass( int i );
3
MyClass( double x, double y );
};
MyClass* a;
a = new MyClass; // Calls 1
a = new MyClass(5); // Calls 2
a = new MyClass( 1.0, 0.0 ); // Calls 3
27
++C
New & arrays
To allocate arrays, use
int n = 4;
int *a = new int[10]; // array of 10
// ints
IntList *b = new IntList[n];
// array of n IntLists
on the heap
int n = 4;
for (int i = 0; i < n; i++)
{
delete (arr[i]);
// invoked the dest. of each MyClass
// object allocated on the heap, and
// free the memory.
}
delete [] arr;
// free the memory allocated for the
// array of pointers. No dest. is invoked
32
++C
33
Copying 1
operator=
34
++C
MyString.h MyString.cpp
#ifndef MYSTRING_H #include “MyString.h”
#define MYSTRING_H
MyString::MyString(char *str)
class MyString {
{ _length = strlen(str)+1;
public: _string = new char[_length];
strcpy(_string,str);
// Constructor };
MyString( char* str );
MyString::~MyString()
// Destructor {
~MyString(); delete[] _string;
}
private:
int _length;
char* _string;
};
#endif
35
++C
operator=
Using MyString we can now write some code,
for example:
MyString str1(“Paul");
MyString str2(“John");
Not Compile?!
36
++C
operator=
Using MyString we can now write some code,
for example:
MyString str1(“Paul");
MyString str2(“John");
It will compile,
however…
37
++C
Few Words on Copy…
38
++C
MyString Revisited
We never defined MyString::operator=
Why our example compiles?
MyString str1(“Paul");
MyString str2(“John");
str1 = str2;
39
++C
MyString Revisited
We never defined MyString::operator=
Why our example compiles?
40
++C
Default operator=
The default operator= copies all data members of the class,
by applying = on each of them (member wise “shallow”
copy)
In our case, the result is:
MyString&
MyString::operator=(const MyString &rhs)
{
_length = rhs._length;
_string = rhs._string;
return *this; // return reference to
// current object
}
41
++C
MyString.h MyString.cpp
#ifndef MYSTRING_H #include “MyString.h”
#define MYSTRING_H
MyString::MyString(char *str)
class MyString {
{ _length = strlen(str)+1;
public: _string = new char[_length];
strcpy(_string,str);
// Constructor };
MyString( char* str );
MyString::~MyString()
// Destructor {
~MyString(); delete[] _string;
}
private:
int _length;
char* _string; MyString str1(“Paul");
}; MyString str2(“John");
str1 = str2;
#endif
42
++C
MyString.h MyString.cpp
#ifndef MYSTRING_H #include “MyString.h”
#define MYSTRING_H
MyString::MyString(char *str)
class MyString {
{ _length = strlen(str)+1;
public: _string = new char[_length];
strcpy(_string,str);
So what is the
// Constructor };
problem
MyString( char* with the
str );
MyString::~MyString()
default
// Destructor {
operator=
~MyString(); delete[] _string;
? }
private:
int _length;
char* _string; MyString str1(“Paul");
}; MyString str2(“John");
str1 = str2;
#endif
43
++C
Example Revisited
void Example()
{
MyString str1("Foo");
MyString str2("Bar");
str1 = str2;
}
44
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo");
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
45
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
46
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
47
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar"); str2 destructor
str1 = str2;
}
:str1 string_ ”Foo“
48
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar"); str2 destructor
str1 = str2; str1 destructor
}
:str1 string_ ”Foo“
49
++C
50
++C
The fix? Define our own operator=
MyString&
MyString::operator=(const MyString &rhs)
{
_length = rhs._length;
_string = new char[_length];
strncpy(_string, rhs._string, _length);
return *this;
}
51
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
52
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2; ”Bar“
}
:str1 string_ ”Foo“
MyString&
MyString::operator=(const MyString
&rhs) :str2 string_ ”Bar“
{
_length = rhs._length;
_string = new char[_length];
strncpy(_string, rhs._string,
_length);
return *this;
53 }
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2; ”Bar“
}
:str1 string_ ”Foo“
MyString&
MyString::operator=(const MyString
&rhs) :str2 string_ ”Bar“
{
_length = rhs._length;
_string = new char[_length];
strncpy(_string, rhs._string,
_length);
return *this;
54 }
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar"); str2 destructor
str1 = str2; ”Bar“
}
:str1 string_ ”Foo“
55
++C
Example Revisited
void Example() str1 constructor
{ str2 constructor
MyString str1("Foo"); str1.operator=(str2)
str2 destructor
MyString str2("Bar"); str1 destructor
str1 = str2;
”Bar“
}
:str1 string_ ”Foo“
56
++C
Can we fix it? Yes!
MyString&
MyString::operator=(const MyString &rhs)
{
delete [] _string;
_length = rhs._length;
_string = new char[_length];
strncpy(_string, rhs._string, _length);
return *this;
}
57
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
58
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
59
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Foo“
60
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar");
str1 = str2;
}
:str1 string_ ”Bar“
61
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar"); str2 destructor
str1 = str2;
}
:str1 string_ ”Bar“
62
++C
Example Revisited
void Example()
{ str1 constructor
MyString str1("Foo"); str2 constructor
str1.operator=(str2)
MyString str2("Bar"); str2 destructor
str1 = str2; str1 destructor
}
:str1 string_ ”Bar“
63
++C
The fix? Define our own operator=
MyString&
MyString::operator=(const MyString &rhs)
{
delete [] _string;
_length = rhs._length;
_string = new char[_length];
strncpy(_string, rhs._string, _length);
return *this;
} Is our code exception safe?
Can you spot another problem?
64
++C
Is This Solution Water Tight?
If a programmer writes:
MyString str("foo");
str = str; // senseless, but legal!
What happens?
• delete str._string
• allocate a new str._string
• Tries to copy this string onto itself… mmm...
65
++C
Checking for Self-Assignment
Add another check at the beginning of the procedure
MyString&
MyString::operator=(const MyString &rhs)
{
if( this == &rhs )
{
return *this;
}
...
66
++C
Are We Out of The Woods?
Consider the following code
void doNothing( MyString S )
{
} S is passed by value,
void anotherExample() copy constructor is
called
{
MyString str("foo");
doNothing(str);
}
Copy Constructor
68
++C
Copy Constructor
Copy constructor:
MyString::MyString(const MyString &init);
69
++C
Copy Constructor
This constructor is also called when initializing
a new object
is equivalent to writing
MyString str2(str1);
In both cases the compiler calls copy
constructor
70
++C
Default Copy Construct
Same as with operator=
If not specified, default instantiation is done by
member wise “shallow” copy
Constructing each member with the value of the
matching field in source
In our example:
MyString::MyString(const MyString &rhs)
:_length(rhs._length),_string(rhs._string)
{ }
71
++C
Are We Out of The Woods?
Consider the following code
void doNothing( MyString S )
{
}
void anotherExample()
{
MyString str("foo");
doNothing(str);
}
73
++C
Example Revisited
S. _string
void doNothing( MyString S )
{
}
”foo“
void anotherExample()
{
MyString str("foo");
str. _string
doNothing(str);
}
74
++C
Fix? Yes
BUT:
initialization list is better for the usual reasons
redundant self assignment check
76
++C
Lessons so far
If a class needs “deep copy” (for example manages
memory), then:
1. Define you own
1. operator=
2. copy constructor
2. Remember to copy all the data members in a
smart way
3. Make sure to check for self-copy
4. Remember that operator= should return a
reference to the object
5. Define a destructor
77
++C
The Rule of Three
78