C++通过空间配置器实现简易String类

C++实现简易String类

在C++中,使用空间配置器(allocator)实现自定义string类需要管理内存分配、释放及对象构造/析构。

#include <memory>
#include <algorithm>
#include <cstring>
#include <stdexcept>
#include <utility>

template<typename CharT, typename Allocator = std::allocator<CharT>>
class MyString {
public:
    using allocator_type = Allocator;
    using traits_type = std::char_traits<CharT>;
    using size_type = typename std::allocator_traits<Allocator>::size_type;

private:
    CharT* data_;
    size_type size_;
    size_type capacity_;
    Allocator allocator_;

    void destroy_elements() {
        if (data_) {
            for (size_type i = 0; i < size_; ++i) {
                std::allocator_traits<Allocator>::destroy(allocator_, data_ + i);
            }
            std::allocator_traits<Allocator>::deallocate(allocator_, data_, capacity_ + 1);
            data_ = nullptr;
            size_ = 0;
            capacity_ = 0;
        }
    }

public:
    MyString() noexcept : data_(nullptr), size_(0), capacity_(0), allocator_() {}

    explicit MyString(const CharT* s, const Allocator& alloc = Allocator())
        : allocator_(alloc) {
        size_ = traits_type::length(s);
        capacity_ = size_;
        if (size_ > 0) {
            data_ = std::allocator_traits<Allocator>::allocate(allocator_, capacity_ + 1);
            std::uninitialized_copy(s, s + size_, data_);
            std::allocator_traits<Allocator>::construct(allocator_, data_ + size_, CharT());
        } else {
            data_ = std::allocator_traits<Allocator>::allocate(allocator_, 1);
            std::allocator_traits<Allocator>::construct(allocator_, data_, CharT());
        }
    }

    MyString(const MyString& other)
        : allocator_(std::allocator_traits<Allocator>::select_on_container_copy_construction(other.allocator_)) {
        size_ = other.size_;
        capacity_ = other.size_;
        if (size_ > 0) {
            data_ = std::allocator_traits<Allocator>::allocate(allocator_, capacity_ + 1);
            std::uninitialized_copy(other.data_, other.data_ + size_, data_);
            std::allocator_traits<Allocator>::construct(allocator_, data_ + size_, CharT());
        } else {
            data_ = std::allocator_traits<Allocator>::allocate(allocator_, 1);
            std::allocator_traits<Allocator>::construct(allocator_, data_, CharT());
        }
    }

    MyString(MyString&& other) noexcept
        : data_(other.data_), size_(other.size_), capacity_(other.capacity_), allocator_(std::move(other.allocator_)) {
        other.data_ = nullptr;
        other.size_ = 0;
        other.capacity_ = 0;
    }

    ~MyString() {
        destroy_elements();
    }

    MyString& operator=(const MyString& other) {
        if (this != &other) {
            MyString temp(other);
            swap(*this, temp);
        }
        return *this;
    }

    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            destroy_elements();
            data_ = other.data_;
            size_ = other.size_;
            capacity_ = other.capacity_;
            allocator_ = std::move(other.allocator_);
            other.data_ = nullptr;
            other.size_ = 0;
            other.capacity_ = 0;
        }
        return *this;
    }

    friend void swap(MyString& a, MyString& b) noexcept {
        using std::swap;
        swap(a.data_, b.data_);
        swap(a.size_, b.size_);
        swap(a.capacity_, b.capacity_);
        swap(a.allocator_, b.allocator_);
    }

    const CharT* c_str() const noexcept {
        return data_ ? data_ : &CharT();
    }

    size_type size() const noexcept {
        return size_;
    }

    size_type capacity() const noexcept {
        return capacity_;
    }

    void reserve(size_type new_capacity) {
        if (new_capacity <= capacity_) return;

        size_type new_size = size_;
        CharT* new_data = std::allocator_traits<Allocator>::allocate(allocator_, new_capacity + 1);

        try {
            std::uninitialized_copy(data_, data_ + size_, new_data);
        } catch (...) {
            std::allocator_traits<Allocator>::deallocate(allocator_, new_data, new_capacity + 1);
            throw;
        }

        std::allocator_traits<Allocator>::construct(allocator_, new_data + new_size, CharT());
        destroy_elements();

        data_ = new_data;
        capacity_ = new_capacity;
    }

    void append(const CharT* s, size_type count) {
        if (count == 0) return;

        if (s >= data_ && s < data_ + size_) {
            size_type offset = s - data_;
            CharT* tmp = new CharT[count];
            traits_type::copy(tmp, data_ + offset, count);
            append_impl(tmp, count);
            delete[] tmp;
        } else {
            append_impl(s, count);
        }
    }

private:
    void append_impl(const CharT* s, size_type count) {
        size_type new_size = size_ + count;
        if (new_size > capacity_) {
            reserve(std::max(new_size, capacity_ * 2));
        }

        std::uninitialized_copy(s, s + count, data_ + size_);
        size_ = new_size;
        std::allocator_traits<Allocator>::construct(allocator_, data_ + size_, CharT());
    }
};

int main() {
    MyString<char> str("Hello");
    str.append(" world!", 7);
    return 0;
}

代码说明:

  1. 成员变量
    • data_:动态分配的字符数组指针。
    • size_:当前字符串长度。
    • capacity_:当前分配的内存容量(不含终止符)。
    • allocator_:空间配置器对象。
  2. 内存管理
    • 使用std::allocator_traits进行内存分配、释放和对象构造/析构。
    • destroy_elements()方法负责销毁对象并释放内存。
  3. 构造函数
    • 默认构造空字符串。
    • 从C风格字符串构造,计算长度并分配内存。
    • 拷贝构造和移动构造正确处理allocator传播。
  4. 赋值运算符
    • 使用拷贝交换惯用法保证异常安全。
    • 移动赋值直接转移资源所有权。
  5. 扩容机制
    • reserve()方法实现内存扩容,使用std::uninitialized_copy迁移数据。
    • append()方法处理自引用情况,避免迭代器失效。
  6. 其他功能
    • c_str()返回C风格字符串。
    • swap()方法交换所有成员,包括allocator。

该实现遵循RAII原则,确保资源安全,且兼容标准allocator机制。实际应用中需进一步处理异常安全、优化性能及支持更多字符串操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值