一些C++中经常会接触的小组件,包括中间件,STL等
但这些内容都只实现了基本功能,实际运用的时候可能还需要根据具体需求进行扩展和优化
内存池 Memory Pool
内存池是一种预先分配一大块内存,然后按需分配小块内存的技术
内存池可以减少频繁的内存分配和释放操作从而提高性能
用一个栈实现:
classDiagram
class MemoryPool {
char* _pool
size_t _objectsize
size_t _totalsize
stack~void*~ _freelist
void* allocate()
void deallocate(void* ptr)
}
MemoryPool --* "1" char : 管理
MemoryPool --* "1" stack : 包含
class FreeListStack {
<>
void*类型
push(void* ptr)
pop() void*
top() void*
empty() bool
}
MemoryPool --> FreeListStack : 使用
hpp
class MemoryPool{
public:
MemoryPool(size_t objectsize, size_t totalsize);
~MemoryPool();
// 用户层api
void* allocate();
void deallocate(void* ptr);
private:
size_t _objectsize; // 单个元素大小
size_t _totalsize; // 预留的个数
char* _pool;
std::stack<void*> _freelist;
};
cpp
#include "MemoryPool.h"
MemoryPool::MemoryPool(size_t objectsize, size_t totalsize)
: _objectsize(objectsize), _totalsize(totalsize)
{
_pool = (char*)malloc(totalsize * objectsize);
if (_pool == nullptr){
throw std::bad_alloc();
}
// 初始化freelist
for (size_t i = 0; i < totalsize; ++i){
_freelist.push(_pool+ i * objectsize);
}
}
MemoryPool::~MemoryPool()
{
free(_pool);
}
void * MemoryPool::allocate()
{
if (_freelist.empty()){
throw std::bad_alloc();
// return nullptr;
}
void* p = _freelist.top();
_freelist.pop();
return p;
}
void MemoryPool::deallocate(void * ptr)
{
_freelist.push(ptr);
return;
}
flowchart TD
A[开始] --> B{根据类型创建内存池}
B -->|分配| C[调用 allocate]
B -->|释放| D[调用 deallocate]
C --> E{_freelist为空?}
E -->|是| F[抛出 bad_alloc]
E -->|否| G[从栈顶取指针]
G --> H[返回指针]
D --> I[指针压入栈]
I --> J[返回]
F --> K[结束]
H --> K
J --> K
使用案例
#include "../inc/test.h"
#include "../inc/MemoryPool.h"
#include <cstring>
class Student
{
public:
char *_name;
int _age;
public:
Student(const char *name, int age) : _age(age) {
_name = new char[strlen(name) + 1];
strcpy(_name, name);
}
~Student() {
delete[] _name;
}
};
int main()
{
try
{
MemoryPool pool(sizeof(Student), 3);
void *mem1 = pool.allocate();
void *mem2 = pool.allocate();
void *mem3 = pool.allocate();
// 定位new表达式,不分配新的内存,直接在提供的内存地址上构造对象
Student *stu1 = new (mem1) Student("Tom", 20);
Student *stu2 = new (mem2) Student("Sam", 22);
Student *stu3 = new (mem3) Student("Tim", 24);
std::cout << "stu1:" << stu1->_name << " " << stu1->_age << std::endl;
std::cout << "stu2:" << stu2->_name << " " << stu2->_age << std::endl;
std::cout << "stu2:" << stu3->_name << " " << stu3->_age << std::endl;
// 显式调用析构
stu1->~Student();
stu2->~Student();
stu3->~Student();
// 池子回收
pool.deallocate(mem1);
pool.deallocate(mem2);
pool.deallocate(mem3);
}
catch (const std::bad_alloc &e)
{
std::cerr << "Memory allocation error: " << e.what() << std::endl;
return 1;
}
return 0;
}
如果是多线程的情况,需要在用户应用层进行线程安全的一系列操作
shared_ptr(共享智能指针)是C++标准库提供的一种智能指针,用于自动管理动态分配的内存资源。它采用引用计数机制,记录有多少个shared_ptr指向同一个对象。当最后一个shared_ptr被销毁或重置时,其所管理的对象会被自动删除,从而有效防止内存泄漏。shared_ptr支持拷贝和赋值操作,允许多个指针共享同一资源。使用shared_ptr能简化资源生命周期管理,是现代C++中避免原始指针风险的重要工具。
这里用一个struct块来模拟引用计数,实际上也可以直接用一个int*来替代。
classDiagram
class MySharedPtr~T~ {
T* ptr
controlblock* ctrl_blk
void Release()
int use_count() const
T* get() const
void reset(T* p)
}
class controlblock {
int count_ref
controlblock()
}
MySharedPtr --o controlblock : 拥有
MySharedPtr --> T : 管理
模板类hpp
#ifndef SHARED_PTR_H
#define SHARED_PTR_H
#include <atomic>
struct controlblock
{
std::atomic<int> count_ref;
controlblock() : count_ref(1) {}
};
template <typename T>
class MySharedPtr
{
private:
T *ptr;
controlblock *ctrl_blk;
/**
* @brief 内部管理计数,当计数降到0时释放内存
*/
void Release()
{
if (ctrl_blk)
{
--ctrl_blk->count_ref;
if (ctrl_blk->count_ref == 0)
{
delete ptr;
delete ctrl_blk;
ptr = nullptr;
ctrl_blk = nullptr;
}
}
}
public:
/**
* @brief 带参数构造
* @param p 指向需要管理的对象的指针
* @return MySharedPtr<T>
* @note 用explicit防止隐式转换
*/
explicit MySharedPtr(T *p = nullptr)
{
{
if (p != nullptr)
{
ptr = p;
ctrl_blk = new controlblock();
}
else
{
ptr = nullptr;
ctrl_blk = nullptr;
}
}
}
/**
* @brief 默认构造
* @param p 指向需要管理的对象的指针
* @return MySharedPtr<T>
*/
MySharedPtr() : ptr(nullptr), ctrl_blk(nullptr) {}
/**
* @brief 析构
*/
~MySharedPtr()
{
if (ptr)
{
Release();
}
}
/**
* @brief 拷贝构造
* @param s 拷贝对象
* @return MySharedPtr<T>
*/
MySharedPtr(const MySharedPtr &s) : ptr(s.ptr), ctrl_blk(s.ctrl_blk)
{
if (ctrl_blk)
ctrl_blk->count_ref++;
}
/**
* @brief 拷贝赋值
* @param s 拷贝对象
* @return MySharedPtr<T>
*/
MySharedPtr &operator=(const MySharedPtr &s)
{
if (&s == this)
return *this;
// 自己引用计数减少
Release();
ptr = s.ptr;
ctrl_blk = s.ctrl_blk;
// 拷贝的引用计数增加
if (ctrl_blk)
ctrl_blk->count_ref++;
}
/**
* @brief 移动构造
* @param s 移动对象
* @return MySharedPtr<T>
*/
MySharedPtr(MySharedPtr &&s) : ptr(s.ptr), ctrl_blk(s.ctrl_blk)
{
s.ptr = nullptr;
s.ctrl_blk = nullptr;
}
/**
* @brief 移动赋值
* @param s 移动对象
* @return MySharedPtr<T>
*/
MySharedPtr &operator=(MySharedPtr &&s)
{
if (&s == this)
return *this;
ptr = s.ptr;
ctrl_blk = s.ctrl_blk;
s.ptr = nullptr;
s.ctrl_blk = nullptr;
}
public:
/**
* @brief ->重载,把类当指针用,方便链式调用
* @return T*
*/
T *operator->() const { return ptr; }
/**
* @brief *重载,取出引用对象
* @return T&
*/
T &operator*() const { return *ptr; }
/**
* @brief 返回引用计数
* @return 引用计数
*/
int use_count() const { return ctrl_blk->count_ref; }
/**
* @brief 返回裸指针
* @return 引用计数
*/
T *get() const { return ptr; }
/**
* @brief 重定向
*/
void reset(T * p){
Release();
ptr = p;
if (p){
ctrl_blk = new controlblock();
}else{
ctrl_blk = nullptr;
}
}
};
#endif
函数封装 Function Encapsulation
四种函数封装方式
- 函数指针
- 仿函数(函数对象)
- std::function
- lambda表达式
- 性能敏感场景:优先考虑函数指针或模板仿函数
- 回调系统:使用
std::function统一接口 - 局部算法:lambda表达式最合适
- 兼容C代码:必须使用函数指针
示例
#ifndef FUNCTION_BIND_H
#define FUNCTION_BIND_H
#include <iostream>
#include <functional>
// 1. 函数指针 - 最基础的函数封装方式
// 优点:简单、高效、零开销
// 缺点:无法捕获上下文、无法处理带状态的函数对象
int (*funcptr)(int, int); // 声明函数指针类型
int add(int a, int b)
{
return a + b;
}
/*
使用示例:
int main() {
funcptr = add; // 指向add函数
int result = funcptr(1, 2); // 调用函数
std::cout << result << std::endl; // 输出: 3
}
*/
// 2. 仿函数(函数对象) - 类/结构体重载operator()
// 优点:可以携带状态、支持模板编程
// 缺点:需要定义完整的类、代码相对冗长
struct adder
{
int to_add;
adder(int value) : to_add(value) {} // 构造函数初始化状态
int operator()(int value) // 重载函数调用运算符
{
return to_add + value;
}
};
/*
使用示例:
int main() {
adder add_five(5); // 创建带状态的函数对象
int result = add_five(10); // 调用operator()
std::cout << result << std::endl; // 输出: 15
}
*/
// 3. std::function - C++11引入的通用函数包装器
// 优点:类型安全、统一接口、可包装任何可调用对象
// 缺点:有一定的运行时开销(类型擦除)
std::function<int(int, int)> func1 = add; // 包装函数指针
std::function<int(int)> func2 = adder(5); // 包装函数对象
std::function<int(int, int)> func3 = // 包装lambda表达式
[](int a, int b) { return a * b; };
// 4. Lambda表达式 - C++11引入的匿名函数
// 优点:语法简洁、可捕获上下文、支持多种捕获模式
// 缺点:捕获复杂时可能降低可读性
auto lambda1 = [](int a, int b) { return a + b; }; // 无捕获
int x = 10;
auto lambda2 = [x](int y) { return x + y; }; // 值捕获
auto lambda3 = [&x](int y) { x += y; return x; }; // 引用捕获
auto lambda4 = [=](int y) { return x + y; }; // 隐式值捕获全部
auto lambda5 = [&](int y) { x += y; return x; }; // 隐式引用捕获全部
#endif
双向链表容器 List
有节点定义,迭代器类和容器类
hpp
#ifndef MYLIST_H
#define MYLIST_H
#include <iostream>
#include <cassert>
template <typename T>
struct ListNode
{
T data;
ListNode *pre;
ListNode *next;
ListNode(const T &value) : data(value), pre(nullptr), next(nullptr) {}
};
template <typename T>
class Myiterator
{
public:
using self_type = Myiterator<T>;
using value_type = T;
using reference = T &;
using pointer = T *;
Myiterator(ListNode<T> *ptr = nullptr) : node_ptr(ptr) {}
reference operator*() const { return node_ptr->data; }
pointer operator->() const { return &(node_ptr->data); }
// ++it
self_type &operator++()
{
if (node_ptr != nullptr)
{
node_ptr = node_ptr->next;
}
return *this;
}
// it++
self_type operator++(int)
{
self_type tmp = *this;
if (node_ptr != nullptr)
{
node_ptr = node_ptr->next;
}
return tmp;
}
self_type &operator--()
{
if (node_ptr != nullptr)
{
node_ptr = node_ptr->pre;
}
return *this;
}
self_type operator--(int)
{
self_type tmp = *this;
--(*this);
return tmp;
}
bool operator==(const self_type &other) const
{
return (other.node_ptr == node_ptr);
}
bool operator!=(const self_type &other) const
{
return (other.node_ptr != node_ptr);
}
private:
/* data */
ListNode<T> *node_ptr;
friend class MyList<T>;
};
template <typename T>
class MyList
{
public:
using iterator = Myiterator<T>;
private:
ListNode<T> *head;
ListNode<T> *tail;
public:
MyList()
{
head = new ListNode<T>();
tail = new ListNode<T>();
head->next = tail;
tail->pre = head;
}
~MyList()
{
clear();
delete head;
delete tail;
}
MyList(const MyList &other) = delete;
MyList &operator=(const MyList &other) = delete;
public:
iterator insert(iterator pos, const T &value)
{
ListNode<T> *node = pos.node_ptr;
ListNode<T> *newnode = new ListNode<T>(value);
ListNode<T> *temp_pre = node->pre;
newnode->next = node;
newnode->pre = temp_pre;
temp_pre->next = newnode;
node->pre = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
ListNode<T> *node = pos.node_ptr;
// 不能删除头尾节点
if (node == head || node == tail)
{
return iterator(tail);
}
node->pre->next = node->next;
node->next->pre = node->pre;
ListNode<T> *nextnode = node->next;
delete node;
return iterator(nextnode);
}
void remove(const T& target){
for (iterator it = begin(); it != end();){
if (*it == target) it = erase(it);
else it++;
}
}
void clear()
{
ListNode<T> *cur = head->next;
while (cur != tail)
{
ListNode<T> *next = cur->next;
delete cur;
cur = next;
}
head->next = tail;
tail->pre = head;
}
bool isempty() const
{
return (head->next == tail);
}
public:
iterator begin()
{
return iterator(head->next);
}
iterator end()
{
return iterator(tail);
}
public:
void push_back(const T &value)
{
insert(end(), value);
}
void push_front(const T &value)
{
insert(begin(), value);
}
void pop_front()
{
if (!isempty())
erase(begin());
}
void pop_back()
{
if (!isempty())
{
iterator tmp = end();
--tmp;
erase(tmp);
}
}
public:
T &front()
{
assert(!isempty() && "Cannot access front of an empty list");
return head->next->data;
}
T &back()
{
assert(!isempty() && "Cannot access back of an empty list");
return tail->pre->data;
}
public:
size_t size() const {
size_t count = 0;
for (iterator it = begin(); it != end(); it++){
count++;
}
return count;
}
};
#endif
双端队列容器 Deque
deque 通常由一系列固定大小的数组块组成,这些块通过一个中央数组进行管理
整个结构使得在两端扩展的时候不需要再重新分配整个容器的内存
简化版:环形缓冲区deque
支持随机访问元素
支持在两端频繁插入和删除元素
实现
#ifndef MYDEQUE_H
#define MYDEQUE_H
#include <iostream>
template <typename T>
class MyDeque
{
private:
T *buffer; // 环形缓冲区
size_t capacity; // 缓冲区容量
size_t count; // 当前元素数量
size_t front_idx; // 前端索引
size_t back_idx; // 下一个插入的位置(即最后一个元素的下一个位置)
public:
MyDeque(size_t initial_capacity = 10) : capacity(initial_capacity),
count(0), front_idx(0), back_idx(0)
{
// 调用T的无参构造
buffer = new T[capacity]();
}
~MyDeque()
{
delete[] buffer;
}
bool isempty() const
{
return count == 0;
}
size_t size() const
{
return count;
}
public:
void resize(size_t new_capacity)
{
T *new_buffer = new T[new_capacity]();
for (size_t i = 0; i < count; i++)
{
new_buffer[i] = buffer[(front_idx + i) % capacity];
}
delete[] buffer;
buffer = new_buffer;
front_idx = 0;
back_idx = count;
capacity = new_capacity;
}
void push_front(const T &value)
{
if (count == capacity)
{
resize(capacity * 2);
}
// 更新front_idx与插入值, 环形逻辑
front_idx = (front_idx - 1 + capacity) % capacity;
buffer[front_idx] = value;
count++;
}
void push_back(const T &value)
{
if (count == capacity)
{
resize(capacity * 2);
}
// 先赋值给当前位置
buffer[back_idx] = value;
// 再移动 back_idx 到下一个位置
back_idx = (back_idx + 1) % capacity;
count++;
}
void pop_front()
{
if (isempty())
{
throw std::out_of_range("Deque is empty. ");
}
front_idx = (front_idx + 1) % capacity;
count--;
}
void pop_back()
{
if (isempty())
{
throw std::out_of_range("Deque is empty. ");
}
back_idx = back_idx == 0 ? capacity - 1 : back_idx - 1;
count--;
}
const T &front() const
{
if (isempty())
{
throw std::out_of_range("Deque is empty. ");
}
return buffer[front_idx];
}
const T &back() const
{
if (isempty())
{
throw std::out_of_range("Deque is empty. ");
}
int last_idx = back_idx == 0 ? capacity - 1 : back_idx - 1;
return buffer[last_idx];
}
// 迭代器
class Myiterator
{
public:
using self_type = MyDeque<T>;
using value_type = T;
using reference = T &;
using pointer = T *;
private:
MyDeque<T> *_deque;
size_t pos;
public:
Myiterator(self_type *ptr = nullptr, size_t position) : _deque(ptr), pos(position) {}
reference operator*() const {
return _deque->buffer[(_deque->front_idx + pos) % _deque->capacity];
}
pointer operator->() const {
return &(_deque->buffer[(_deque->front_idx + pos) % _deque->capacity]);
}
// 前置++
Myiterator &operator++() {
pos++;
return *this;
}
// 后置++
Myiterator operator++(int) {
Myiterator tmp = *this;
pos++;
return tmp;
}
bool operator==(const Myiterator &other) const {
return _deque == other._deque && pos == other.pos;
}
bool operator!=(const Myiterator &other) const {
return !(*this == other);
}
};
MyDeque begin(){
return Myiterator(this, 0);
}
};
#endif