1 of 35

Dynamic Memory (Pt.3)

C. Papachristos

Robotic Workers Lab

University of Nevada, Reno

CS-202

2 of 35

Course , Projects , Labs:

​

​

​

​

Your 7th Project Deadline is this Wednesday 4/1.

​

  • PASS Sessions held as announced, get all the help you need!

  • 24-hrs delay after Project Deadline incurs 20% grade penalty.
  • Past that, NO Project accepted. Better send what you have in time!

Monday

Tuesday

Wednesday

Thursday

Friday

​

Sunday

 

 

 

Lab (8 Sections)

 

​

​

 

CLASS

PASS

Session

CLASS

 

​

​

PASS

Session

​

Project DEADLINE

NEW Project

 PASS

Session

​

 PASS

Session

Course Week

CS-202 C. Papachristos

3 of 35

Today’s Topics

CS-202 C. Papachristos

Overloading operator new ([]) and operator delete ([])

  • Globally
  • As Class static Methods

​

Storage and Alignment

  • Specifier alignas()
  • Over-Alignment

​

Alignment and Dynamic Memory

  • Alignment-Controlled Allocation

​

Placement new ([]) ( and Placement delete ([]) ).

  • Placement operator new ([]) a
  • Placement operator delete ([])

​

​

4 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Remember: The Basics

​

There is no named Object / Variable : All work is done on a Pointer-basis.

  • Allocation reserves memory space.
  • Address of reserved space is returned.
  • Marked as “containing a specific data type” (int, double, struct, class, arrays, etc.)

​

All facilitated by the “behind-the-scenes” use of C++ Dynamic Memory (De-)Allocation functions:

​

Operator new dynamically Allocates memory space.

void * operator new (std::size_t count);

void * operator new [] (std::size_t count);

​

Operator delete can free-up this space (Deallocate memory) later on.

void operator delete (void * ptr);

void operator delete [] (void * ptr);

5 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What if we want to implement our own special low-level memory management ?

  • C++ Dynamic Memory (De-)Allocation are overload-eable.

​

They can be overloaded:

​

  1. Globally :

Affecting Dynamic Memory Management across our entire program.

​

  1. As static Class Methods :

Affecting the Dynamic Memory Management pertaining to that particular Class.

6 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * operator new ([]) (std::size_t count);

​

void operator delete ([]) (void * ptr);

​

​

a) Globally :

  • Specify how Dynamic Memory Management takes place across our entire program.

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

void * operator new(std::size_t size) {

cout<<"Dyn.Alloc. Object of size: "<< size <<endl;

void * p = malloc(size);

if (p == nullptr) {

cout<<"Dyn.Alloc. failed, throwing..."<<endl;

throw std::bad_alloc();

}

return p;

}

​

Size of requested dynamic memory

Pointer to successfully allocated memory

Pointer of dynamic memory to release

void * operator new [](std::size_t size) {

cout<<"Dyn.Alloc. Object Array of size: "<< size <<endl;

void * p = malloc(size);

if (p == nullptr) {

cout<<"Dyn.Alloc. failed, throwing..."<<endl;

throw std::bad_alloc();

}

return p;

}

7 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * operator new ([]) (std::size_t count);

​

void operator delete ([]) (void * ptr);

​

​

a) Globally :

  • Specify how Dynamic Memory Management takes place across our entire program.

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

void operator delete(void * ptr) {

cout<<"Dyn.Dealloc. Object at address: "<< ptr <<endl;

if (ptr == (void *)0xDEADBEEF) {

cout<<"CAUTION: delete called on dead pointer"<<endl;

}

else {

free(ptr);

}

}

​

Size of requested dynamic memory

Pointer to successfully allocated memory

Pointer of dynamic memory to release

void operator delete [](void * ptr) {

cout<<"Dyn.Dealloc. Object Array at address: "<< ptr <<endl;

if (ptr == (void *)0xDEADBEEF) {

cout<<"CAUTION: delete [] called on dead pointer"<<endl;

}

else {

free(ptr);

}

}

Note: Function takes the Pointer by-Value, you cannot reassign its value internally and have the change persist.�(i.e., you can’t do ptr = nullptr; to automatically handled all released memory pointers)

8 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

a) Globally :

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

void * operator new(std::size_t size) { … }

void * operator new [](std::size_t size) { … }

void operator delete(void * p) { … }

void operator delete [](void * p) { … }

​

int main() {

MyStruct * p_ms = new MyStruct(1, 0.1);

cout<<"(*p_ms).a,b : "<<p_ms->a<<","<<p_ms->b<<endl;

​

delete p_ms;

​

int * p_i = new int(1);

cout<<"*p_i : "<< *p_i <<endl;

​

delete p_i; p_i = (int *)0xDEADBEEF; delete p_i;

​

int * pa_i;

try { pa_i = new int[2305843009213693950]; }

catch (const std::bad_alloc & e) {

pa_i = nullptr;

cout<< e.what() <<endl;

}

​

delete [] pa_i;

}

struct MyStruct {

MyStruct(int a_in, double b_in) : a(a_in) , b(b_in) { }

int a;

double b;

double c[10];

};

Dyn.Alloc. Object of size: 96

​

(*p_ms).a,b : 1, 0.1

​

Dyn.Dealloc. Object at address: 0x800012930

1)

2)

3)

9 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

a) Globally :

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

void * operator new(std::size_t size) { … }

void * operator new [](std::size_t size) { … }

void operator delete(void * p) { … }

void operator delete [](void * p) { … }

​

int main() {

MyStruct * p_ms = new MyStruct(1, 0.1);

cout<<"(*p_ms).a,b : "<<p_ms->a<<","<<p_ms->b<<endl;

​

delete p_ms;

​

int * p_i = new int(1);

cout<<"*p_i : "<< *p_i <<endl;

​

delete p_i; p_i = (int *)0xDEADBEEF; delete p_i;

​

int * pa_i;

try { pa_i = new int[2305843009213693950]; }

catch (const std::bad_alloc & e) {

pa_i = nullptr;

cout<< e.what() <<endl;

}

​

delete [] pa_i;

}

struct MyStruct {

MyStruct(int a_in, double b_in) : a(a_in) , b(b_in) { }

int a;

double b;

double c[10];

};

Dyn.Alloc. Object of size: 4

​

*p_i : 1

​

Dyn.Dealloc. Object at address: 0x8000004b0

​

Dyn.Dealloc. Object at address: 0xdeadbeef

CAUTION: delete called on dead pointer

1)

2)

3)

4)

10 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

a) Globally :

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

void * operator new(std::size_t size) { … }

void * operator new [](std::size_t size) { … }

void operator delete(void * p) { … }

void operator delete [](void * p) { … }

​

int main() {

MyStruct * p_ms = new MyStruct(1, 0.1);

cout<<"(*p_ms).a,b : "<<p_ms->a<<","<<p_ms->b<<endl;

​

delete p_ms;

​

int * p_i = new int(1);

cout<<"*p_i : "<< *p_i <<endl;

​

delete p_i; p_i = (int *)0xDEADBEEF; delete p_i;

​

int * pa_i;

try { pa_i = new int[2305843009213693950]; }

catch (const std::bad_alloc & e) {

pa_i = nullptr;

cout<< e.what() <<endl;

}

​

delete [] pa_i;

}

struct MyStruct {

MyStruct(int a_in, double b_in) : a(a_in) , b(b_in) { }

int a;

double b;

double c[10];

};

Dyn.Alloc. Object Array of size: 9223372036854775800

​

Dyn.Alloc. failed, throwing...

std::bad_alloc

​

​

1)

2)

3)

? Why is there no output here even though we delete [] ?

​

We invoked the delete [] Expression on a “Null Pointer”,�which does not call the function operator delete [] !

11 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * Car :: operator new ([]) (std::size_t count);

​

void Car :: operator delete ([]) (void * ptr);

​

​

b) As static Class Methods :

  • Specify how Dynamic Memory Management takes place for that particular Class.

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() { cout<<"Car Default"<<endl; } ...

void * operator new(std::size_t size) {

cout<<"Dyn.Alloc. Car Object of size: "<< size <<endl;

void * p = malloc(size);

if (p == nullptr) {

cout<<"Dyn.Alloc. failed, throwing..."<<endl;

throw std::bad_alloc();

}

return p;

}

};

​

Declared within the scope of a Class

void * operator new [](std::size_t size) {

cout<<"Dyn.Alloc. Car Object Array of size: "<< size <<endl;

void * p = malloc(size);

if (p == nullptr) {

cout<<"Dyn.Alloc. failed, throwing..."<<endl;

throw std::bad_alloc();

}

return p;

}

IMPORTANT Note:�As class overloads, they will be implicitly declared static !�(whether you do so or not …)

i)

i)

Provide your own highly customized low-level Dynamic Memory Management for your class.

12 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * Car :: operator new ([]) (std::size_t count);

​

void Car :: operator delete ([]) (void * ptr);

​

​

b) As static Class Methods :

  • Specify how Dynamic Memory Management takes place for that particular Class.

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() { cout<<"Car Default"<<endl; } ...

void * operator new(std::size_t size) {

cout<<"Dyn.Alloc. Car Object of size: "<< size <<endl;

void * p = ::operator new(size);

return p;

}

};

​

Declared within the scope of a Class

void * operator new [](std::size_t size) {

cout<<"Dyn.Alloc. Car Object Array of size: "<< size <<endl;

void * p = ::operator new [](size);

return p;

}

IMPORTANT Note:�As class overloads, they will be implicitly declared static !�(whether you do so or not …)

Re-use the global C++ Dynamic Memory Allocation function ( ::f qualifies the name f in the Global Namespace) �Note 1: You might have overloaded the Global one as well.

Note 2: The default operator new ([]) is expected to throw.

ii)

ii)

13 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * Car :: operator new ([]) (std::size_t count);

​

void Car :: operator delete ([]) (void * ptr);

​

​

b) As static Class Methods :

  • Specify how Dynamic Memory Management takes place for that particular Class.

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() { cout<<"Car Default"<<endl; } ...

void * operator new(std::size_t size) {

cout<<"Dyn.Alloc. Car Object of size: "<< size <<endl;

void * p = ::new Car;

return p;

}

};

​

Declared within the scope of a Class

void * operator new [](std::size_t size) {

cout<<"Dyn.Alloc. Car Object Array of size: "<< size <<endl;

void * p = ::new Car[ size / sizeof(Car) ];

return p;

}

IMPORTANT Note:�As class overloads, they will be implicitly declared static !�(whether you do so or not …)

Re-use the global new ([]) Expression ( ::f qualifies the name f in the Global Namespace) �

Bad Design, something like: Car * c_pt = new Car; will end up calling a ctor twice per Object,�as a side-effect of executing 2 subsequent new ([]) Expressions !

iii)

iii)

14 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Breakdown:

void * Car :: operator new ([]) (std::size_t count);

​

void Car :: operator delete ([]) (void * ptr);

​

​

b) As static Class Methods :

  • Specify how Dynamic Memory Management takes place for that particular Class.

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() { cout<<"Car Default"<<endl; } ...

void operator delete(void * ptr) {

cout<<"Dyn.Dealloc. Car Object at address: "<<ptr<<endl;

::delete ptr;

::operator delete(size);

if (ptr == (void *)0xDEADBEEF)

cout<<"CAUTION: delete called on dead ptr"<<endl;

else

free(ptr);

}

};

Declared within the scope of a Class

void operator delete [](void * ptr) {

cout<<"Dyn.Alloc. Car Object Array at address: "<<ptr<<endl;

::delete [] ptr;

::operator delete [](ptr);

if (ptr == (void *)0xDEADBEEF)

cout<<"CAUTION: delete [] called on dead ptr"<<endl;

else

free(ptr);

}

IMPORTANT Note:�As class overloads, they will be implicitly declared static !�(whether you do so or not …)

i)

iii)

ii)

i)

iii)

ii)

Similarly as before for sample implementations i) ii) iii)

15 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

b) As static Class Methods :

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

int main() {

​

Car * p_c = new Car(1000.0);

cout << "(*p_c).getPrice() : " <<

p_c->getPrice() << endl;

​

delete p_c;

p_c = (Car *)0xDEADBEEF;

​

Car * pa_c = new Car[10];

cout << "pa_c[9].getPrice() : " <<

pa_c[9].getPrice() << endl;

​

delete [] pa_c;

p_ac = (Car *)0xDEADBEEF;

​

return 0;

}

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { …(i)… }

void * operator new [](std::size_t size) { …(i)… }

void operator delete(void * p) { …(i)… }

void operator delete [](void * p) { …(i)… }

};

Dyn.Alloc. Car Object of size: 24

Car Param

​

(*p_c).getPrice() : 1000

​

Car Destructor

Dyn.Dealloc. Car Object at address: 0x8000004b0

1)

2)

3)

...

16 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

b) As static Class Methods :

​

#include <iostream>

#include <cstdlib>

using namespace std;

​

int main() {

​

Car * p_c = new Car(1000.0);

cout << "(*p_c).getPrice() : " <<

p_c->getPrice() << endl;

​

delete p_c;

p_c = (Car *)0xDEADBEEF;

​

Car * pa_c = new Car[10];

cout << "pa_c[9].getPrice() : " <<

pa_c[9].getPrice() << endl;

​

delete [] pa_c;

p_ac = (Car *)0xDEADBEEF;

​

return 0;

}

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { …(i)… }

void * operator new [](std::size_t size) { …(i)… }

void operator delete(void * p) { …(i)… }

void operator delete [](void * p) { …(i)… }

};

Dyn.Alloc. Car Object Array of size: 248

Car Default (x10)

​

pa_c[9].getPrice() : 1.34497e-284

​

Car Destructor (x10)

Dyn.Alloc. Car Object Array at address: 0x80005ac70

1)

2)

3)

...

17 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first see at some more depth the previous examples:

​

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { … void * p = malloc(size); … }

void * operator new [](std::size_t size) { … void * p = malloc(size); … }

void operator delete(void * p) { … free(p); … }

void operator delete [](void * p) { … free(p); … }

};

Dyn.Alloc. Car Object of size: 24

...

But the memory footprint should be 18 :

1 x sizeof(double) + 10 x sizeof(char)

​

double m_price; char m_licensePlates[10];

​

Right?

Dyn.Alloc. Car Object Array of size: 248

But the memory footprint should be 240 :

… = new Car[10];

​

Right?

18 of 35

Explanation :

The only reasonable way to expect the program during�runtime to know there is an array of a certain size there, is to�store extra information !

Extra Note :

If we had no user-declared dtor, then the size would be 240, as the�Car object being a data-only (1 double + 10 chars) structure means�the Deallocation of this memory wouldn’t need to do anything about their destruction (even for 10 x Cars).

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

​

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { … void * p = malloc(size); … }

void * operator new [](std::size_t size) { … void * p = malloc(size); … }

void operator delete(void * p) { … free(p); … }

void operator delete [](void * p) { … free(p); … }

};

...

Dyn.Alloc. Car Object Array of size: 248

Actually has memory footprint:

8

bytes

24

bytes

24

bytes

...

24

bytes

24

bytes

​

​

​

​

​

​

Car [10]

how big?

19 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

​

​

class Car {

private: double m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { … void * p = malloc(size); … }

void * operator new [](std::size_t size) { … void * p = malloc(size); … }

void operator delete(void * p) { … free(p); … }

void operator delete [](void * p) { … free(p); … }

};

Dyn.Alloc. Car Object of size: 24

...

Actually requires memory layout:

8

bytes

8

bytes

8

bytes

​

​

​

Explanation :

The compiler “pads” the memory of our class / struct members to make the object aligned to 8-byte boundaries.

Extra Info :

sizeof( Car ); 24:A Car object takes up 24 bytes

alignof( Car ); 8 :Car objects are placed at 8-byte boundaries

​

double�m_price;

char�m_licesePlates[10]

Padding

20 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

​

​

class Car {

private: float m_price; char m_licensePlates[10];

public: Car() {cout<<"Car Default"<<endl;}

Car(double price) : m_price(price) {cout<<"Car Param"<<endl;}

~Car() {cout<<"Car Destructor"<<endl;}

​

void * operator new(std::size_t size) { … void * p = malloc(size); … }

void * operator new [](std::size_t size) { … void * p = malloc(size); … }

void operator delete(void * p) { … free(p); … }

void operator delete [](void * p) { … free(p); … }

};

Dyn.Alloc. Car Object of size: 16

...

Changing double→float requires memory layout:

4 bytes

4 bytes

4

bytes

4

bytes

​

​

​

​

float�m_price

char�m_licesePlates[10]

Padding

Explanation :

The compiler “pads” the memory of our class / struct members to make the object aligned to 4-byte boundaries.

Extra Info :

sizeof( Car ); 16:A Car object takes up 16 bytes

alignof( Car ); 4 :Car objects are placed at 4-byte boundaries

​

21 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

Even more interestingly …

​

​

struct S1 {

char c1;

double d;

char c2;

};

​

alignof(S1):8

sizeof(S1) :24

Based on the structure:

1 char → 1 byte + 7 padding

1 double → 8 bytes

1 char → 1 byte + 7 padding

struct S2 {

char c1;

char c2;

double d;

};

​

alignof(S2):8

sizeof(S2) :16

Based on the structure:

2 chars → 2 bytes + 6 padding

1 double → 8 bytes

( ! )

22 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

Even more interestingly …

​

​

struct S1 {

char c1;

double d;

char c2;

};

​

alignof(S1):8

sizeof(S1) :24

Based on the structure:

1 char → 1 byte + 7 padding

1 double → 8 bytes

1 char → 1 byte + 7 padding

struct S2 {

char c1;

char c2;

double d;

};

​

alignof(S2):8

sizeof(S2) :16

Based on the structure:

2 chars → 2 bytes + 6 padding

1 double → 8 bytes

struct S3 {

char c1;

float f;

char c2;

};

​

alignof(S3):4

sizeof(S3) :12

Based on the structure:

1 char → 1 byte + 3 padding

1 float → 4 bytes

1 char → 1 byte + 3 padding

struct S4 {

char c1;

char c2;

float f;

};

​

alignof(S4):4

sizeof(S4) :8

Based on the structure:

2 chars → 2 bytes + 2 padding

1 float → 4 bytes

( ! )

( ! )

23 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Let’s first examine a bit more curiously the previous examples:

Even more interestingly …

​

​

struct S1 {

char c1;

double d;

char c2;

};

​

alignof(S1):8

sizeof(S1) :24

Based on the structure:

1 char → 1 byte + 7 padding

1 double → 8 bytes

1 char → 1 byte + 7 padding

struct S2 {

char c1;

char c2;

double d;

};

​

alignof(S2):8

sizeof(S2) :16

Based on the structure:

2 chars → 2 bytes + 6 padding

1 double → 8 bytes

struct S3 {

char c1;

float f;

char c2;

};

​

alignof(S3):4

sizeof(S3) :12

Based on the structure:

1 char → 1 byte + 3 padding

1 float → 4 bytes

1 char → 1 byte + 3 padding

struct S4 {

char c1;

char c2;

float f;

};

​

alignof(S4):4

sizeof(S4) :8

Based on the structure:

2 chars → 2 bytes + 2 padding

1 float → 4 bytes

struct alignas(8) S5 {

char c1;

float f;

char c2;

};

​

alignof(S5):8

sizeof(S5) :16

Based on the structure:

1 char → 1 byte + 3 padding

1 float → 4 bytes

1 char → 1 byte + 3 padding

+ Even more padding … (*)

alignas(X)

( ! )

( ! )

C++ allows us to “Over-Align” data !

( * ) Probably so, as the C++ standard doesn’t specify how padding should be performed…

24 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • What is the benefit of controlling Alignment ?�I) Over-Alignment :�� a) Can grant some (very niche) binary-level data interoperability between different architectures.�� No guarantees as there is not standard-specified rule about how individual data padding takes place!��� b) Can yield huge performance benefits, when working with big data (e.g., a large double array)� and we make use of specific CPU instruction sets (e.g., Architecture-dependent optimizations).�� Examples:� The SSE (128-bit register based) instruction sets assume 16-byte aligned data.� The AVX (256-bit register based) instruction set assumes 32-byte aligned data.

​

Note:

In reality, useless

Note:

They move�the world !

25 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • What is the benefit of controlling Alignment ?�II) Dynamic Memory Alignment :�� To leverage: a) such special optimization benefits, as well as � b) for general memory addressing optimization,� the starting memory address of our big data arrays also has to be aligned at proper boundaries. �� But the C++ standard doesn’t specify how the default Allocation Function (our non-overloaded� vanilla operator new ([]) ) should align !�� Might be implemented in terms of simple malloc(), yielding no specific alignment results …�� new double[100]; Might align 100 x doubles on the Freestore at the convenient for natural� addressing 4 – or – 8-byte boundary (32-bit / 64-bit), but also might NOT.

​

Note: Stack-allocated objects however will be properly aligned !

26 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • What is the benefit of controlling Alignment ?�II) Dynamic Memory Alignment :�� This is why we choose to provide our own Memory Allocation overloads.

​

  • Considerations:

� a) We could overload the Global Memory Allocation to perform native (or some other special) alignment� Then new double[100]; , new int[100]; , … would all use that desired alignment.� But, we probably don’t want to do so as as the side-effects would be too broad.�� b) But if a particular class can leverage some performance-critical optimizations,� or we can “wrap” our desired datatype (the one we want to specially align) into a class / struct, � we can then overload that class’ / struct’s own operator new ([]) to perform some specific� type of Memory-Aligned Allocation … �

​

27 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

Controling Alignment – By-Example:��new double[100];

alignof(double):8

sizeof(double) :8�Dynamically Allocated array:�size: 100 x 4, but the alignment�of each of its elements on the�Freestore not guaranteed�to be at 8-byte boundaries�(if the starting address of the�first element happens to be�8-byte aligned, then yes, but�it is not guaranteed)

struct alignas(16) double_sse128 {

double data;

​

//overload operator new ([]) to control memory allocation alignment

void * operator new(std::size_t size) {

cout<<"Allocating "<< alignof(double_sse128) <<"-aligned. Object of size"<<size<<endl;

void * p = aligned_alloc(alignof(double_sse128), size);

if (p == nullptr) { throw std::bad_alloc(); }

return p;

}

void * operator new [](std::size_t size) {

cout<<"Allocating "<< alignof(double_sse128) <<"-aligned. Object Array of size"<<size<<endl;

void * p = aligned_alloc(alignof(double_sse128), size);

if (p == nullptr) { throw std::bad_alloc(); }

return p;

}

};

�Therefore we have for our custom double-wrapper type:

alignof(double_sse128): 16 , sizeof(double_sse128): 16

​

And will be aligned as desired whenever Dynamically Allocated !

28 of 35

Dynamic Memory Allocation

CS-202 C. Papachristos

Overloading operator new ([]) / operator delete ([])

​

What is the major benefit of having such flexibility ?

  • Further benefits:�Fully-Controlled Low-Level Dynamic Memory Management

​

It is often desired in classes to manage all Dynamic Memory-relevant functionality� at the Low Level, in order to optimize its efficiency:

� In such cases it is common to wish to control allocation through explicitly chosen C low-level functions.� An example would be a class that wraps a dynamic container:� class DynamicContainer {� private: double * m_data;� public: …

};�� For such a case, the C low-level function realloc() would offer huge benefits, but designing it� into our class also requires to intervene at various levels of Dynamic Memory Management� (i.e. a simple new double[size] is not the way to go)…

Used to manage data of dynamically-determined sizes,�which can grow or shrink during runtime.

29 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement new ([ ]) Expression

​

Uses a special overload of operator new ([ ]) to construct an Object in the Freestore, at a known and properly pre-Allocated memory location.

​

  • Uses that memory location to just Construct the Object !
  • Avoid the performance impact of Dynamic Allocation in the Freestore.� Consider new double[100000000]; Requires almost 1GB to be found during runtime !� Can have pre-Allocated large Memory pools ready-to go !
  • The corresponding Placement operator new ([ ]) overloads invoked during Placement new ([]) :�void * operator new (std::size_t count, void * place);

void * operator new [] (std::size_t count, void * place);

​

  • If the pointer is a “Null Pointer” i.e. nullptr/NULL then we have Undefined Behavior.

​

The pointer that�Placement new ([])�received, forwarded.

30 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement new ([ ]) Expression

​

Syntax:

<type_id> * new (place_ptr) <type_id_ctor> ([SIZE] : optional)

​

Example:

Single Object

void * mem_mC = ::operator new( sizeof(MyClass) );

​

MyClass * myClass_Pt = new (mem_mC) MyClass("mine",1);

​

Allocate storage using the Global Allocation function

Construct Object in-Place

31 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement new ([ ]) Expression

​

Syntax:

<type_id> * new (place_ptr) <type_id_ctor> ([SIZE] : optional)

​

Example:

Single Object

void * mem_mC = ::operator new( sizeof(MyClass) );

​

MyClass * myClass_Pt = new (mem_mC) MyClass("mine",1);

​

Object Array is NOT so simple …

void * mem_mCA = ::operator new[](100 * sizeof(MyClass));

​

MyClass * myClassArr_Pt = new (mem_mCA) MyClass [100];

Allocate storage using the Global Allocation function

Construct Object in-Place

Remember, how do we know it’s just a multiple of an Object?

How do we know there’s no initial padding?

32 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement new ([ ]) Expression

​

Syntax:

<type_id> * new (place_ptr) <type_id_ctor> ([SIZE] : optional)

​

Example:

Single Object

void * mem_mC = ::operator new( sizeof(MyClass) );

​

MyClass * myClass_Pt = new (mem_mC) MyClass("mine",1);

​

Simple Datatype Arrays are safer …

void * char_buf = ::operator new[](100 * sizeof(char));

​

char * myDynamicCstr_Pt =� new (char_buf) char [100]{'L','o','r','e','m',…,'r','u','m','\0'};

Allocate storage using the Global Allocation function

Construct Object in-Place

For simpler built-in types we can be safer allocating memory buffers.

33 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement delete ([ ]) Expression

​

It really doesn’t exist in the C++ standard.�What we have are special overloads of operator delete ([ ]) that are invoked whenever Placement new ([]) throws an Exception…

​

  • If the Object ctor invoked by the Placement new ([]) throws an Exception,�then these special overloads are called :�void operator delete (void * ptr, void * place);

void operator delete [] (void * ptr, void * place);

​

  • The standard library implementation does nothing …
  • But you can provide your overloads (e.g., in debugging-relevant applications).

​

34 of 35

Dynamic Memory Management

CS-202 C. Papachristos

The Placement new ([ ]) Expression

​

Example Usage (Single Object):��Version 1: General Memory Buffer ( void * )

�void * mem_mC = ::operator new( sizeof(MyClass) );

MyClass * myClass_Pt = new (mem_mC) MyClass("mine",1);

�myClass_Pt->~MyClass();�::operator delete( mem_mC );�

Version 2: Byte-sized Type Memory Buffer ( char * )�

char * mem_mC = ::new char[ sizeof(MyClass) ];

MyClass * myClass_Pt = new (mem_mC) MyClass("mine",1);

�myClass_Pt->~MyClass();�::delete[]( mem_mC );

35 of 35

Time for Questions !

CS-202

CS-202 C. Papachristos