跳转至

自定义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 原理相似。

  1. 内部缓冲区: std::function 对象内部拥有一小块预留的内存(比如16或32字节)。
  2. 存储策略:

  3. 如果要存入的可调用对象(比如一个Lambda)体积很小,能够完全放进这块内部缓冲区,就不会有任何堆分配。

  4. 如果这个可调用对象体积太大(通常是因为它捕获了大量的外部变量),超出了内部缓冲区的容量,std::function 在堆上分配一块内存来存储它,然后在内部记录一个指向这块堆内存的指针。

替代实现:https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h

注意:std::function会带来动态多态,造成性能开销和不确定性,仍然需要考虑使用编译期多态方法替代使用。