adding the emplace_back and push_back example programs
This commit is contained in:
57
lectures/optimization/emplace_back/emplace_back_faster.cpp
Normal file
57
lectures/optimization/emplace_back/emplace_back_faster.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// move constructor
|
||||
// marking functions as noexcept allows the compiler to make certain optimizations,
|
||||
// knowing that the function won't emit exceptions. This can result in more efficient code generation.
|
||||
LargeObject(LargeObject&& other) noexcept {
|
||||
size_ = other.size_;
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i) {
|
||||
vec.emplace_back(dataSize); // move constructor would get called here
|
||||
// when a std::vector exceeds its current capacity,
|
||||
// it allocates a larger block of memory and moves existing elements to the new storage location.
|
||||
// This reallocation process involves calling the move constructor for each existing element.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
lectures/optimization/emplace_back/emplace_back_fastest.cpp
Normal file
55
lectures/optimization/emplace_back/emplace_back_fastest.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// move constructor
|
||||
// marking functions as noexcept allows the compiler to make certain optimizations,
|
||||
// knowing that the function won't emit exceptions. This can result in more efficient code generation.
|
||||
LargeObject(LargeObject&& other) noexcept {
|
||||
size_ = other.size_;
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
vec.reserve(numElements);
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i) {
|
||||
vec.emplace_back(dataSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
47
lectures/optimization/emplace_back/emplace_back_slower.cpp
Normal file
47
lectures/optimization/emplace_back/emplace_back_slower.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
|
||||
for (size_t i = 0; i < numElements; ++i) {
|
||||
vec.emplace_back(dataSize); // copy constructor would get called here
|
||||
// when a std::vector exceeds its current capacity,
|
||||
// it allocates a larger block of memory and copies existing elements to the new storage location.
|
||||
// this reallocation process involves calling the copy constructor for each existing element.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
lectures/optimization/emplace_back/push_back.cpp
Normal file
44
lectures/optimization/emplace_back/push_back.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
|
||||
for (int i = 0; i < numElements; ++i) {
|
||||
LargeObject obj(dataSize); // calls constructor
|
||||
vec.push_back(obj); // calls copy constructor
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
54
lectures/optimization/emplace_back/push_back_move.cpp
Normal file
54
lectures/optimization/emplace_back/push_back_move.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// move constructor
|
||||
// marking functions as noexcept allows the compiler to make certain optimizations,
|
||||
// knowing that the function won't emit exceptions. This can result in more efficient code generation.
|
||||
LargeObject(LargeObject&& other) noexcept {
|
||||
size_ = other.size_;
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
|
||||
for (int i = 0; i < numElements; ++i) {
|
||||
LargeObject obj(dataSize); // calls constructor
|
||||
vec.push_back(std::move(obj)); // calls move constructor
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
lectures/optimization/emplace_back/push_back_reserve.cpp
Normal file
55
lectures/optimization/emplace_back/push_back_reserve.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
class LargeObject {
|
||||
public:
|
||||
// constructor allocating a large amount of memory
|
||||
LargeObject(int size) {
|
||||
size_ = size;
|
||||
data_ = new char[size_];
|
||||
// initialize data with some values
|
||||
std::memset(data_, 'A', size_);
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
LargeObject(const LargeObject& other) {
|
||||
size_ = other.size_;
|
||||
data_ = new char[size_];
|
||||
std::memcpy(data_, other.data_, size_);
|
||||
}
|
||||
|
||||
// move constructor
|
||||
// marking functions as noexcept allows the compiler to make certain optimizations,
|
||||
// knowing that the function won't emit exceptions. This can result in more efficient code generation.
|
||||
LargeObject(LargeObject&& other) noexcept {
|
||||
size_ = other.size_;
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
// destructor
|
||||
~LargeObject() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
char* data_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
int numElements = 1000000; // number of elements
|
||||
int dataSize = 1024; // size of each LargeObject's data
|
||||
|
||||
std::vector<LargeObject> vec;
|
||||
vec.reserve(numElements);
|
||||
|
||||
for (int i = 0; i < numElements; ++i) {
|
||||
LargeObject obj(dataSize); // calls constructor
|
||||
vec.push_back(std::move(obj)); // calls move constructor
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
85
lectures/optimization/push_back/README.md
Normal file
85
lectures/optimization/push_back/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# `emplace_back` vs. `push_back` in C++
|
||||
|
||||
## Overview
|
||||
|
||||
Both `push_back` and `emplace_back` are member functions of C++ standard library containers (e.g., `std::vector`, `std::deque`) used to add elements to the end of the container. However, they differ in how they construct and insert these elements.
|
||||
|
||||
## `push_back`
|
||||
|
||||
The `push_back` function adds an existing object to the end of the container. It requires the object to be constructed before being passed to the function.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```cpp
|
||||
std::vector<MyClass> vec;
|
||||
MyClass obj(args);
|
||||
vec.push_back(obj); // Adds a copy of 'obj' to the vector
|
||||
```
|
||||
|
||||
If the object is movable, push_back can utilize move semantics:
|
||||
|
||||
```cpp
|
||||
vec.push_back(std::move(obj)); // Moves 'obj' into the vector
|
||||
```
|
||||
|
||||
## `emplace_back`
|
||||
|
||||
The emplace_back function constructs a new element in place at the end of the container. It forwards the provided arguments to the constructor of the element, eliminating the need for a temporary object.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```cpp
|
||||
std::vector<MyClass> vec;
|
||||
vec.emplace_back(args); // Constructs 'MyClass' directly in the vector
|
||||
```
|
||||
|
||||
This approach can improve performance by avoiding unnecessary copy or move operations, especially for complex objects.
|
||||
|
||||
## Key Differences
|
||||
|
||||
- **Object Construction:**
|
||||
- `push_back`: Requires a fully constructed object.
|
||||
- `emplace_back`: Constructs the object in place using provided arguments.
|
||||
|
||||
- **Performance:**
|
||||
- `push_back`: May involve copy or move operations, depending on whether the object is passed by value or moved.
|
||||
- `emplace_back`: Potentially more efficient for complex objects, as it avoids extra copy or move operations.
|
||||
|
||||
## When to Use
|
||||
|
||||
- **Use `push_back`** when you have an existing object that you want to add to the container.
|
||||
|
||||
- **Use `emplace_back`** when you want to construct a new object directly in the container, especially if the object's construction is complex or resource-intensive.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass(int id, const std::string& name) : id_(id), name_(name) {}
|
||||
private:
|
||||
int id_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::vector<MyClass> vec;
|
||||
|
||||
// Using push_back
|
||||
MyClass obj(1, "Object1");
|
||||
vec.push_back(obj); // Adds a copy of 'obj'
|
||||
|
||||
// Using emplace_back
|
||||
vec.emplace_back(2, "Object2"); // Constructs 'MyClass(2, "Object2")' in place
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In this example, emplace_back constructs the MyClass object directly within the vector, potentially reducing overhead compared to push_back, which adds a copy of an existing object.
|
||||
|
||||
For a visual explanation and further insights, consider watching the following video: [](https://www.youtube.com/watch?v=BbPWrkgj1I4)
|
||||
|
||||
Reference in New Issue
Block a user