C++ STL替代方案技术咨询:无STL实现核心功能的方案问询
C++ STL替代方案技术咨询:无STL实现核心功能的方案问询
完全懂你这种纠结——C++的底层控制感和语法灵活性确实香,但STL的复杂度(尤其是某些容器的黑箱行为、模板膨胀带来的二进制体积问题)真的容易让人头大。我自己在做轻量级嵌入式项目、追求极致性能的服务器程序时,也会刻意避开STL,下面就把我常用的替代方案给你掰扯清楚,全是实战里验证过的:
动态字符串
完全可以自己封装一个极简版本,核心就是管理char*的内存、长度和容量,逻辑比STL的std::string透明太多:
struct DynString { char* data; size_t len; size_t cap; DynString() : data(nullptr), len(0), cap(0) {} ~DynString() { free(data); } // 追加字符串的核心逻辑 void append(const char* s) { size_t s_len = strlen(s); if (len + s_len >= cap) { // 扩容策略自定义,我一般用2倍扩容(也可以按固定步长,比如每次加1024字节) cap = (len + s_len + 1) * 2; data = (char*)realloc(data, cap); // 注意:realloc失败会返回nullptr,生产环境要加判断 } memcpy(data + len, s, s_len); len += s_len; data[len] = '\0'; // 手动补结束符 } // 也可以加个c_str()方法直接返回原生指针 const char* c_str() const { return data ? data : ""; } };
实用提示:如果是嵌入式场景,把realloc换成平台提供的内存分配接口(比如单片机里的heap_caps_malloc)就行,适配性拉满。
动态数组
和动态字符串思路一致,用模板做泛型支持(不用STL的模板特性,自己写极简模板就行):
template<typename T> struct DynArray { T* data; size_t count; size_t capacity; DynArray() : data(nullptr), count(0), capacity(0) {} ~DynArray() { delete[] data; } void push_back(const T& val) { if (count >= capacity) { // 初始容量设4,之后2倍扩容 capacity = capacity == 0 ? 4 : capacity * 2; T* new_data = new T[capacity]; // 手动拷贝元素:POD类型用memcpy更高效,非POD类型调用拷贝构造 if (std::is_trivially_copyable_v<T>) { memcpy(new_data, data, count * sizeof(T)); } else { for (size_t i = 0; i < count; ++i) { new_data[i] = data[i]; } } delete[] data; data = new_data; } data[count++] = val; } T& operator[](size_t idx) { return data[idx]; } const T& operator[](size_t idx) const { return data[idx]; } };
实用提示:如果是纯C风格的项目,把模板换成void*,再额外传元素大小,用memcpy做拷贝,兼容性更强。
计时
直接用平台原生API,比STL的std::chrono更直接,没有模板层的开销:
Windows 平台
#include <windows.h> // 返回微秒级时间戳 long long get_timestamp() { LARGE_INTEGER freq, counter; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&counter); return (counter.QuadPart * 1000000) / freq.QuadPart; }
Linux 平台
#include <time.h> // 返回微秒级时间戳(用CLOCK_MONOTONIC避免系统时间被修改影响计时) long long get_timestamp() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; }
线程与同步
同样用平台原生接口,比STL的std::thread、std::mutex更可控:
Windows 平台
#include <windows.h> // 线程函数 DWORD WINAPI worker_thread(LPVOID param) { int* task_data = (int*)param; // 这里写线程逻辑,比如处理任务数据 *task_data += 10; return 0; } // 使用示例 int main() { int data = 5; HANDLE thread = CreateThread(nullptr, 0, worker_thread, &data, 0, nullptr); WaitForSingleObject(thread, INFINITE); // 等待线程结束 CloseHandle(thread); return 0; } // 同步用CriticalSection(轻量级)或Event(异步通知) CRITICAL_SECTION cs; InitializeCriticalSection(&cs); EnterCriticalSection(&cs); // 临界区代码 LeaveCriticalSection(&cs); DeleteCriticalSection(&cs);
Linux 平台
#include <pthread.h> // 线程函数 void* worker_thread(void* param) { int* task_data = (int*)param; *task_data += 10; return nullptr; } // 使用示例 int main() { int data = 5; pthread_t tid; pthread_create(&tid, nullptr, worker_thread, &data); pthread_join(tid, nullptr); // 等待线程结束 return 0; } // 同步用pthread_mutex_t pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex); // 临界区代码 pthread_mutex_unlock(&mutex); pthread_mutex_destroy(&mutex);
内存管理
除了直接用malloc/free、new/delete,高频场景下我会自己写极简内存池,减少碎片和分配开销:
// 针对固定大小对象的内存池(适合频繁创建销毁同类型小对象) struct FixedMemPool { void* free_list; size_t obj_size; FixedMemPool(size_t obj_sz) : free_list(nullptr), obj_size(obj_sz) {} void* allocate() { // 优先从空闲链表取 if (free_list) { void* ptr = free_list; free_list = *(void**)ptr; // 链表头后移 return ptr; } // 没有空闲块就从堆上分配 return malloc(obj_size); } void deallocate(void* ptr) { if (!ptr) return; // 把释放的块挂到空闲链表头部 *(void**)ptr = free_list; free_list = ptr; } };
其他常用功能(比如字符串格式化)
不用STL的stringstream,直接用C标准库的vsnprintf封装:
#include <stdarg.h> // 简单的字符串格式化函数 int str_format(char* buf, size_t buf_size, const char* fmt, ...) { va_list args; va_start(args, fmt); int ret = vsnprintf(buf, buf_size, fmt, args); va_end(args); return ret; // 返回写入的字符数(不含结束符) }
其实核心思路就是用平台原生API+极简封装,完全避开STL的复杂度,同时保留C++的底层控制感。如果是特定场景(比如嵌入式),还可以把内存分配换成平台提供的内存池(比如RTOS的动态内存接口),适配性拉满。有具体场景的话,还能再细化优化细节~




