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 doublefloat requires memory layout:

4 bytes

4 bytes

4

bytes

4

bytes

floatm_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 AlignmentBy-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