0% found this document useful (0 votes)
55 views

04 - DynamicMemoryRuleOfThree

The document introduces the C++ new and delete operators. It explains that new calls the operator new to allocate memory and then calls the constructor to construct an object at the allocated space. Delete calls the destructor and then frees the memory. New can throw exceptions on failure by default but exceptions can be disabled. New can be used with arrays as well as single objects. Classes rely on new and delete to properly allocate and construct objects with their resources initialized and freed.

Uploaded by

Ido Akov
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
55 views

04 - DynamicMemoryRuleOfThree

The document introduces the C++ new and delete operators. It explains that new calls the operator new to allocate memory and then calls the constructor to construct an object at the allocated space. Delete calls the destructor and then frees the memory. New can throw exceptions on failure by default but exceptions can be disabled. New can be used with arrays as well as single objects. Classes rely on new and delete to properly allocate and construct objects with their resources initialized and freed.

Uploaded by

Ido Akov
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 78

++C

Introduction to C++

Programming Workshop in C++ (67317)


2020
++C

operator = and copy


constructor

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 &);

This is why we can write:


int a, b;
(a = b) = 5;
(a=b)++;
5
++C
What about classes?
The same operator for a class X would have the type
signature
X& X::operator=(const X&);
Why?
• const X& argument?
To ensure that the right-hand side does not
change
Avoid copying
• The X& return type?
To allow for (x=y)=z, like the built-in types

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:

Complex& Complex::operator=(const Complex &c)


{
   _real = c._real;
   _img = c._img;
   return *this; // return reference to
    // current object
}

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

Copy constructor in case of a pass or return by value


Consider the following code
void doNothing(Complex c)
{
} c is passed by value,
int main() copy constructor is
called
{
   Complex a(1,3);
   doNothing(a);
return 0;
}
What is going on here?
9
++C

Copy constructor in case of a pass or return by value


Consider the following code
void doNothing(Complex c )
{
} Complex dtor called for c //
int anotherExample()
{ Complex ctor called for a //
   Complex a(1,3);
   doNothing(a);
Complex ctor called for c //
return 0;
} Complex dtor called for a //
10
++C
Complex returnCopy(const Complex& c)
{
Complex a = c; How many calls to the
return a; copy constructor do we
have here?
} A. 1
B. 2
int main() C. 3
{ 61%
(45)

   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?

• Explicit call MyClass a(b);


• Parameter passed to function by value
• Parameter passed to function by reference
• Return value from function by value
• Return value from function by reference
• in the command MyClass a=b;
(b is also of type MyClass)
• in the command MyClass a, b; a = b;
( expect the case that operator = is implemented by
calling copy constructor)

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

Dynamic Memory Allocation

new / delete keywords

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);
} }

What is the difference?


19
++C
Classes & Memory allocation
IntList* L =
(IntList*)malloc(sizeof(IntList));
Does not call constructor!
Internal data members are not initialized

free(L);
Does not call destructor!
Internal data members are not freed

20
++C
new & delete
Special operators:

IntList *L = new IntList;


1. Allocate memory
2. Call constructor (IntList())
3. Return pointer to the constructed object

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

• std::nothrow constant is used as an argument for operator


new to indicate that these functions shall not throw an
exception on failure, but return a null pointer instead.

• By default a bad_alloc exception is thrown on failure


24
++C
new expression
// calls operator new (sizeof(T))
new T;

// calls operator new[] (sizeof(T)*5)


new T[5];

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

28 Objects in allocated array must have an


++C
Delete & array
Special operation to delete arrays

int *a = new int[10];


int *b = new int[10];

delete[] a; // proper delete command

For an array of objects of type MyClass, will also call the


destructor of each of the objects in the array before
releasing the actual memory segment.
29
++C
Delete & array
Special operation to delete arrays

int *a = new int[10];


int *b = new int[10];

delete[] a; // proper delete command


delete b; // undefined: avoid it!
All life comes to a catastrophic end!
Will compile! May work, but may cause memory leak, and
maybe heap corruption, and maybe your program will die!
30
++C
Allocate array of objects w/o def. cons.
int n = 4;
MyClass **arr = new MyClass *[n];
// array of n pointers to MyClass (no
// cons. is invoked)

for (int i = 0; i < n; i++)


{
   arr[i] = new MyClass (i);
   // each pointer points to a MyClass
// object allocated on the heap, and
// the cons. is invoked.
}
31
Free an allocated array of pointers to objects ++C

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

new & delete – keep a watch!


For every new, you should call delete
For every new[], you should call delete[]

You can take advantage of the option to


override the new and delete related
operators, to keep track on memory
management.
However, valgrind is a tool that does
that for you – use it to check your
program.

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");

// What will this do?


str1 = str2;

Not Compile?!

36
++C
operator=
Using MyString we can now write some code,
for example:

MyString str1(“Paul");
MyString str2(“John");

// What will this do?


str1 = str2;

It will compile,
however…
37
++C
Few Words on Copy…

What does the assignment str1 = str2 do?


High-level view:
“=“ is an operator (like “+”)
The command specified here can also be written as:
str1.operator=(str2)

Function name Argument

The compiler searches for a myString method function


(operator =) with argument of type MyString

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?

The compiler defines default instantiation of


operator=
This happens for every class for which the
programmer did not define operator=
Saves unneeded work for many classes

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“

:str2 string_ ”Bar“

46
++C
Example Revisited
void Example()
{ str1 constructor
   MyString str1("Foo"); str2 constructor
str1.operator=(str2)
   MyString str2("Bar");
   str1 = str2;
}
:str1 string_ ”Foo“

:str2 string_ ”Bar“

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“

:str2 string_ ”Bar“

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“

:str2 string_ ”Bar“


Problems!
• A memory location is deleted twice!
• Memory leak!

49
++C

How can we fix this?

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“

:str2 string_ ”Bar“

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“

:str2 string_ ”Bar“

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“

Memory leak  :str2 string_ ”Bar“

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“

:str2 string_ ”Bar“

58
++C
Example Revisited
void Example()
{ str1 constructor
   MyString str1("Foo"); str2 constructor
str1.operator=(str2)
   MyString str2("Bar");
   str1 = str2;
}
:str1 string_ ”Foo“

:str2 string_ ”Bar“

59
++C
Example Revisited
void Example()
{ str1 constructor
   MyString str1("Foo"); str2 constructor
str1.operator=(str2)
   MyString str2("Bar");
   str1 = str2;
}
:str1 string_ ”Foo“

:str2 string_ ”Bar“

60
++C
Example Revisited
void Example()
{ str1 constructor
   MyString str1("Foo"); str2 constructor
str1.operator=(str2)
   MyString str2("Bar");
   str1 = str2;
}
:str1 string_ ”Bar“

:str2 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“

:str2 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“

:str2 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);
}

What is going on here?


67
Copying 2

Copy Constructor

68
++C
Copy Constructor

What happens when we call DoNothing(str)?


A new MyString object is created on the stack.
 This object needs to be constructed
 It also needs to copy the value of str

Copy constructor:
MyString::MyString(const MyString &init);

69
++C
Copy Constructor
This constructor is also called when initializing
a new object

MyString str2 = str1;

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);
}

What is wrong with this picture?


72
++C
Are We Out of The Woods?
Consider the following code
void doNothing( MyString S )
{
} MyString dtor called for S //
void anotherExample()
{ MyString ctor called for str //
   MyString str("foo");
   doNothing(str);
MyString ctor called for S //
}
MyString dtor called for 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 

MyString::MyString(const MyString &rhs)


{
   _length = rhs._length;
   _string = new char[_length];
strncpy(_string, rhs._string, _length);
}

What about self copy here?


No problem
75
++C
Can we do it cleaner?

MyString::MyString(const MyString &rhs)


{
this->operator=(rhs);   
}

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

Sometimes you need to implement a class that


manages a resource.
In that case, remember the rule of three:

If you need to explicitly declare either:


 the destructor,
 copy constructor, or
 copy assignment operator,
you probably need to explicitly declare all three of
them

78

You might also like