自定义string实现¶
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "macros.h"
// Type alias for easy usage with custom allocator
// Example usage:
// PinnedArena arena(1024 * 1024);
// LowLatencyAllocator<char> alloc(arena);
// using FastString = MyString<23, LowLatencyAllocator<char>>;
// FastString str(alloc);
template <size_t SSO_CAPACITY = 23, typename Allocator = void>
class MyString {
private:
struct LongString {
char* ptr;
size_t size;
size_t capacity;
};
union Data {
LongString l;
char s[SSO_CAPACITY + 1];
} data_;
void set_short_size(size_t size) {
data_.s[SSO_CAPACITY] = static_cast<char>(SSO_CAPACITY - size);
}
bool is_short() const { return LIKELY(data_.s[SSO_CAPACITY] != 0); }
size_t get_short_size() const { return SSO_CAPACITY - data_.s[SSO_CAPACITY]; }
void set_long_flag() { data_.s[SSO_CAPACITY] = 0; }
void destroy_long_string() {
if (UNLIKELY(!is_short())) {
std::free(data_.l.ptr);
}
}
public:
// --- 构造函数和析构函数 ---
explicit MyString(const Allocator& = Allocator()) noexcept {
data_.s[0] = '\0';
set_short_size(0);
}
MyString(const char* str, const Allocator& = Allocator()) {
size_t len = std::strlen(str);
if (LIKELY(len <= SSO_CAPACITY)) {
std::memcpy(data_.s, str, len);
data_.s[len] = '\0';
set_short_size(len);
} else {
data_.l.ptr = static_cast<char*>(std::malloc(len + 1));
std::memcpy(data_.l.ptr, str, len);
data_.l.ptr[len] = '\0';
data_.l.size = len;
data_.l.capacity = len;
set_long_flag();
}
}
~MyString() { destroy_long_string(); }
// --- 拷贝和移动 ---
MyString(const MyString& other) {
if (LIKELY(other.is_short())) {
std::memcpy(&data_, &other.data_, sizeof(data_));
} else {
const auto& other_l = other.data_.l;
data_.l.ptr = static_cast<char*>(std::malloc(other_l.capacity + 1));
std::memcpy(data_.l.ptr, other_l.ptr, other_l.size + 1);
data_.l.size = other_l.size;
data_.l.capacity = other_l.capacity;
set_long_flag();
}
}
MyString(MyString&& other) noexcept {
std::memcpy(&data_, &other.data_, sizeof(data_));
other.data_.s[0] = '\0';
other.set_short_size(0);
}
MyString& operator=(const MyString& other) {
if (this == &other) return *this;
if (LIKELY(other.is_short())) {
destroy_long_string();
std::memcpy(&data_, &other.data_, sizeof(data_));
} else {
destroy_long_string();
const auto& other_l = other.data_.l;
data_.l.ptr = static_cast<char*>(std::malloc(other_l.capacity + 1));
std::memcpy(data_.l.ptr, other_l.ptr, other_l.size + 1);
data_.l.size = other_l.size;
data_.l.capacity = other_l.capacity;
set_long_flag();
}
return *this;
}
MyString& operator=(MyString&& other) noexcept {
if (this == &other) return *this;
destroy_long_string();
std::memcpy(&data_, &other.data_, sizeof(data_));
other.data_.s[0] = '\0';
other.set_short_size(0);
return *this;
}
// --- 访问器 ---
const char* c_str() const noexcept {
return LIKELY(is_short()) ? data_.s : data_.l.ptr;
}
size_t size() const noexcept {
return LIKELY(is_short()) ? get_short_size() : data_.l.size;
}
Allocator get_allocator() const { return Allocator{}; }
};
#include <iostream>
#include "MyString.hpp"
int main() {
// 演示短字符串 (在栈上,无堆分配)
MyString<> s1 = "hello world";
std::cout << "Short string s1: " << s1.c_str() << " | Size: " << s1.size()
<< std::endl;
std::cout << "sizeof(s1): " << sizeof(s1) << " bytes" << std::endl;
// 演示长字符串 (在堆上分配)
MyString<> s2 = "This is a much longer string that exceeds the SSO capacity.";
std::cout << "Long string s2: " << s2.c_str() << " | Size: " << s2.size()
<< std::endl;
std::cout << "sizeof(s2): " << sizeof(s2) << " bytes" << std::endl;
return 0;
}
zhixuan@gpu04:~$ ./test
Short string s1: hello world | Size: 11
sizeof(s1): 24 bytes
Long string s2: This is a much longer string that exceeds the SSO capacity. | Size: 59
sizeof(s2): 24 bytes
// 对短字符串避免堆分配:低延迟场景必要。
// 对象尺寸统一:确保了可预测的内存布局和性能。
std::function 是一个类型擦除的通用可调用对象包装器。一个 std::function<void(int)> 既可以存储一个普通函数指针,也可以存储一个函数对象(Functor),或者是一个Lambda表达式。为了实现通用性,std::function 内部采用了一种叫做 小缓冲优化 (Small Buffer Optimization, SBO) 的技术,与 SSO String 原理相似。
- 内部缓冲区:
std::function对象内部拥有一小块预留的内存(比如16或32字节)。 -
存储策略:
-
如果要存入的可调用对象(比如一个Lambda)体积很小,能够完全放进这块内部缓冲区,就不会有任何堆分配。
- 如果这个可调用对象体积太大(通常是因为它捕获了大量的外部变量),超出了内部缓冲区的容量,
std::function在堆上分配一块内存来存储它,然后在内部记录一个指向这块堆内存的指针。
替代实现:https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h
注意:std::function会带来动态多态,造成性能开销和不确定性,仍然需要考虑使用编译期多态方法替代使用。