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;
}
代码说明:
- 成员变量:
data_
:动态分配的字符数组指针。size_
:当前字符串长度。capacity_
:当前分配的内存容量(不含终止符)。allocator_
:空间配置器对象。
- 内存管理:
- 使用
std::allocator_traits
进行内存分配、释放和对象构造/析构。 destroy_elements()
方法负责销毁对象并释放内存。
- 使用
- 构造函数:
- 默认构造空字符串。
- 从C风格字符串构造,计算长度并分配内存。
- 拷贝构造和移动构造正确处理allocator传播。
- 赋值运算符:
- 使用拷贝交换惯用法保证异常安全。
- 移动赋值直接转移资源所有权。
- 扩容机制:
reserve()
方法实现内存扩容,使用std::uninitialized_copy
迁移数据。append()
方法处理自引用情况,避免迭代器失效。
- 其他功能:
c_str()
返回C风格字符串。swap()
方法交换所有成员,包括allocator。
该实现遵循RAII原则,确保资源安全,且兼容标准allocator机制。实际应用中需进一步处理异常安全、优化性能及支持更多字符串操作。