C++
Overview
Modern C++ (C++11 and beyond) patterns including RAII, smart pointers, templates, and STL.
Modern C++ Fundamentals
Smart Pointers
#include <memory> #include <iostream>
// unique_ptr - exclusive ownership class Resource { public: Resource() { std::cout << "Resource acquired\n"; } ~Resource() { std::cout << "Resource released\n"; } void use() { std::cout << "Resource used\n"; } };
void unique_ptr_example() { // Create unique_ptr auto ptr = std::make_unique<Resource>(); ptr->use();
// Transfer ownership
auto ptr2 = std::move(ptr);
// ptr is now nullptr
// Array support
auto arr = std::make_unique<int[]>(10);
}
// shared_ptr - shared ownership void shared_ptr_example() { auto shared1 = std::make_shared<Resource>(); { auto shared2 = shared1; // Reference count = 2 shared2->use(); } // shared2 destroyed, count = 1
std::cout << "Use count: " << shared1.use_count() << "\n";
} // shared1 destroyed, resource released
// weak_ptr - non-owning reference class Node { public: std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // Avoid circular reference
~Node() { std::cout << "Node destroyed\n"; }
};
void weak_ptr_example() { auto node1 = std::make_shared<Node>(); auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->prev = node1; // weak_ptr, no ownership
if (auto locked = node2->prev.lock()) {
// Use locked (shared_ptr)
}
}
RAII Pattern
#include <fstream> #include <mutex>
// File wrapper with RAII class File { std::fstream file_;
public: explicit File(const std::string& filename) : file_(filename, std::ios::in | std::ios::out) { if (!file_.is_open()) { throw std::runtime_error("Failed to open file"); } }
~File() {
if (file_.is_open()) {
file_.close();
}
}
// Delete copy operations
File(const File&) = delete;
File& operator=(const File&) = delete;
// Allow move operations
File(File&& other) noexcept : file_(std::move(other.file_)) {}
File& operator=(File&& other) noexcept {
file_ = std::move(other.file_);
return *this;
}
void write(const std::string& data) {
file_ << data;
}
};
// Lock guard (RAII for mutex) class ThreadSafeCounter { mutable std::mutex mutex_; int count_ = 0;
public: void increment() { std::lock_guard<std::mutex> lock(mutex_); ++count_; }
int get() const {
std::lock_guard<std::mutex> lock(mutex_);
return count_;
}
};
// Scoped cleanup template<typename F> class ScopeGuard { F cleanup_; bool active_ = true;
public: explicit ScopeGuard(F cleanup) : cleanup_(std::move(cleanup)) {}
~ScopeGuard() {
if (active_) cleanup_();
}
void dismiss() { active_ = false; }
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
};
// Usage void example() { auto resource = acquireResource(); ScopeGuard guard(& { releaseResource(resource); });
// Do work...
guard.dismiss(); // Don't cleanup if successful
}
Move Semantics
#include <vector> #include <string> #include <utility>
class Buffer { std::unique_ptr<char[]> data_; size_t size_;
public: // Constructor explicit Buffer(size_t size) : data_(new char[size]), size_(size) {}
// Copy constructor
Buffer(const Buffer& other) : data_(new char[other.size_]), size_(other.size_) {
std::copy(other.data_.get(), other.data_.get() + size_, data_.get());
}
// Move constructor
Buffer(Buffer&& other) noexcept
: data_(std::move(other.data_)), size_(other.size_) {
other.size_ = 0;
}
// Copy assignment
Buffer& operator=(const Buffer& other) {
if (this != &other) {
data_.reset(new char[other.size_]);
size_ = other.size_;
std::copy(other.data_.get(), other.data_.get() + size_, data_.get());
}
return *this;
}
// Move assignment
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
data_ = std::move(other.data_);
size_ = other.size_;
other.size_ = 0;
}
return *this;
}
size_t size() const { return size_; }
};
// Perfect forwarding template<typename T, typename... Args> std::unique_ptr<T> make_unique_custom(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }
Templates
Function Templates
#include <type_traits> #include <concepts>
// Basic template template<typename T> T max(T a, T b) { return (a > b) ? a : b; }
// Template specialization template<> const char* max<const char*>(const char* a, const char* b) { return (strcmp(a, b) > 0) ? a : b; }
// SFINAE (Substitution Failure Is Not An Error) template<typename T> typename std::enable_if<std::is_integral<T>::value, T>::type double_value(T value) { return value * 2; }
// C++20 Concepts template<typename T> concept Numeric = std::is_arithmetic_v<T>;
template<Numeric T> T add(T a, T b) { return a + b; }
// Requires clause template<typename T> requires std::is_default_constructible_v<T> T create_default() { return T{}; }
// Variadic templates template<typename... Args> void print(Args... args) { (std::cout << ... << args) << "\n"; }
// Fold expressions template<typename... Args> auto sum(Args... args) { return (args + ...); }
Class Templates
// Generic container template<typename T, size_t N> class Array { T data_[N];
public: constexpr size_t size() const { return N; }
T& operator[](size_t index) {
if (index >= N) throw std::out_of_range("Index out of range");
return data_[index];
}
const T& operator[](size_t index) const {
if (index >= N) throw std::out_of_range("Index out of range");
return data_[index];
}
T* begin() { return data_; }
T* end() { return data_ + N; }
const T* begin() const { return data_; }
const T* end() const { return data_ + N; }
};
// Template with default arguments template<typename T, typename Allocator = std::allocator<T>> class Vector { // ... };
// Partial specialization template<typename T> class Container<T*> { // Specialization for pointer types };
// CRTP (Curiously Recurring Template Pattern) template<typename Derived> class Counter { static inline int count_ = 0;
public: Counter() { ++count_; } ~Counter() { --count_; }
static int count() { return count_; }
};
class Widget : public Counter<Widget> { // Widget inherits counting behavior };
STL Containers and Algorithms
#include <vector> #include <map> #include <unordered_map> #include <set> #include <algorithm> #include <numeric>
void container_examples() { // vector std::vector<int> vec{1, 2, 3, 4, 5}; vec.push_back(6); vec.emplace_back(7); // Construct in place
// map
std::map<std::string, int> ordered_map;
ordered_map["one"] = 1;
ordered_map.insert({"two", 2});
ordered_map.try_emplace("three", 3);
// unordered_map
std::unordered_map<std::string, int> hash_map;
hash_map["one"] = 1;
// set
std::set<int> ordered_set{3, 1, 4, 1, 5};
auto [iter, inserted] = ordered_set.insert(9);
}
void algorithm_examples() { std::vector<int> vec{5, 2, 8, 1, 9, 3};
// Sort
std::sort(vec.begin(), vec.end());
std::sort(vec.begin(), vec.end(), std::greater<int>());
// Find
auto it = std::find(vec.begin(), vec.end(), 8);
auto it2 = std::find_if(vec.begin(), vec.end(), [](int n) { return n > 5; });
// Transform
std::vector<int> doubled(vec.size());
std::transform(vec.begin(), vec.end(), doubled.begin(), [](int n) { return n * 2; });
// Accumulate
int sum = std::accumulate(vec.begin(), vec.end(), 0);
// Remove-erase idiom
vec.erase(std::remove_if(vec.begin(), vec.end(), [](int n) { return n < 3; }), vec.end());
// C++20 ranges (simplified)
// auto result = vec | std::views::filter([](int n) { return n > 3; })
// | std::views::transform([](int n) { return n * 2; });
}
Concurrency
#include <thread> #include <future> #include <mutex> #include <condition_variable> #include <atomic>
// Basic threading void thread_example() { std::thread t( { std::cout << "Hello from thread\n"; }); t.join(); }
// async/future std::future<int> async_example() { return std::async(std::launch::async, { std::this_thread::sleep_for(std::chrono::seconds(1)); return 42; }); }
// promise/future void promise_example() { std::promise<int> promise; std::future<int> future = promise.get_future();
std::thread producer([&promise]() {
promise.set_value(42);
});
int result = future.get();
producer.join();
}
// Thread-safe queue template<typename T> class ThreadSafeQueue { std::queue<T> queue_; mutable std::mutex mutex_; std::condition_variable cond_;
public: void push(T value) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(std::move(value)); cond_.notify_one(); }
T pop() {
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]() { return !queue_.empty(); });
T value = std::move(queue_.front());
queue_.pop();
return value;
}
bool try_pop(T& value) {
std::lock_guard<std::mutex> lock(mutex_);
if (queue_.empty()) return false;
value = std::move(queue_.front());
queue_.pop();
return true;
}
};
// Atomic operations class AtomicCounter { std::atomic<int> count_{0};
public: void increment() { count_.fetch_add(1, std::memory_order_relaxed); } int get() const { return count_.load(std::memory_order_relaxed); } };
Lambda Expressions
#include <functional>
void lambda_examples() { // Basic lambda auto add = [](int a, int b) { return a + b; };
// Capture by value
int x = 10;
auto by_value = [x]() { return x; };
// Capture by reference
auto by_ref = [&x]() { x++; };
// Capture all by value
auto all_value = [=]() { return x; };
// Capture all by reference
auto all_ref = [&]() { x++; };
// Mutable lambda (modify captured values)
auto mutable_lambda = [x]() mutable { return ++x; };
// Generic lambda (C++14)
auto generic = [](auto a, auto b) { return a + b; };
// Init capture (C++14)
auto ptr = std::make_unique<int>(42);
auto capture_move = [p = std::move(ptr)]() { return *p; };
// Template lambda (C++20)
auto template_lambda = []<typename T>(std::vector<T>& vec) {
return vec.size();
};
// Constexpr lambda (C++17)
constexpr auto square = [](int n) constexpr { return n * n; };
static_assert(square(5) == 25);
}
// Storing lambdas class EventHandler { std::function<void(int)> handler_;
public: void set_handler(std::function<void(int)> handler) { handler_ = std::move(handler); }
void trigger(int value) {
if (handler_) handler_(value);
}
};
Error Handling
#include <stdexcept> #include <optional> #include <variant> #include <expected> // C++23
// Custom exception class DatabaseError : public std::runtime_error { int error_code_;
public: DatabaseError(const std::string& message, int code) : std::runtime_error(message), error_code_(code) {}
int error_code() const { return error_code_; }
};
// std::optional for nullable values std::optional<int> find_value(const std::string& key) { if (key == "answer") return 42; return std::nullopt; }
void optional_usage() { auto result = find_value("answer");
if (result) {
std::cout << "Found: " << *result << "\n";
}
int value = result.value_or(0);
}
// std::variant for type-safe union using Result = std::variant<int, std::string>;
Result compute(bool success) { if (success) return 42; return std::string("error"); }
void variant_usage() { Result r = compute(true);
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) {
std::cout << "Success: " << arg << "\n";
} else {
std::cout << "Error: " << arg << "\n";
}
}, r);
}
// std::expected (C++23) // std::expected<int, std::string> divide(int a, int b) { // if (b == 0) return std::unexpected("Division by zero"); // return a / b; // }
Related Skills
-
[[system-design]] - Systems programming
-
[[performance-optimization]] - Low-level optimization
-
[[desktop-apps]] - Native applications