【C++】string类模拟实现:探索其内部机制

string本质使一个类,虽然std::string内部确实使用了字符数组来存储字符串数据,但它远不止是一个简单的char*封装。它提供了一个完整、安全和方便的字符串操作接口,是C++中处理字符串的首选方式。

底层这块的实现:

C++ 的 std::string 类在底层通常是通过一个字符数组来存储字符串的字符序列,以及一个表示字符串长度的成员变量(有些实现可能还会有一个表示分配容量的成员变量)。这种实现方式允许 std::string 以连续的内存块来存储字符,从而实现高效的随机访问和修改操作。

std::string类是一个模板类特化std::basic_string <T>的具体化,通常定义为std::basic_string<char>。

容器类 basic_string的模板声明如下:

template<class _Elem,
class _Traits,
class _Ax>
class basic_string

在该模板定义中,最重要的参数是第一个: _Elem,它指定了 basic_string 对象将存储的数据类型。

因此, std::string 使用_Elem=char 具体化模板 basic_string 的结果,而 wstring 使用_Elem= wchar 具体化

模板 basic_string 的结果。

typedef basic_string<char, char_traits<char>, allocator<char> >
string;

而 STL wstring 类的定义如下:

typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >
string;

实现的时候需要考虑关键部分:

  1. 字符数组:用于存储实际的字符数据。这个数组的大小通常会在运行时动态调整,以容纳更多的字符或节省内存空间。
  2. 长度信息:一个整数,表示当前字符串中的字符数。这个信息用于快速获取字符串的长度,而不需要遍历整个字符数组。
  3. 容量信息(可选):std::string内部跟踪它当前存储的字符数量(大小)以及分配的总容量(容量)。这允许它在需要时重新分配内存,同时优化性能。
  4. 异常安全性:与直接使用char*相比,std::string是异常安全的。这意味着在执行字符串操作时,即使发生异常,std::string也能保证内存的正确管理,避免内存泄漏或悬挂指针。
  5. 兼容性std::string与C风格的字符串兼容,可以通过c_str()成员函数获取一个指向内部字符数组的常量指针,这在需要与C风格的API交互时非常有用。

内存管理

std::string 使用了一种称为“小对象优化”(Small Object Optimization, SOO)或“短字符串优化”(Short String Optimization, SSO)的技术来提高效率。这种技术的基本思想是,对于较短的字符串,直接在 std::string 对象内部存储字符数据,而不是动态分配内存。这样可以减少内存分配和释放的开销,提高短字符串处理的效率。

当字符串的长度超过某个阈值时(这个阈值通常是实现定义的),std::string 会动态分配一个更大的内存块来存储字符数据,并将长度和容量信息相应地更新为新的值。当字符串被修改或操作时,如果需要的内存空间超过了当前分配的容量,std::string 会重新分配一个更大的内存块,并将原有的字符数据复制到新的内存块中。

复制和赋值

当复制或赋值一个 std::string 对象时,通常会涉及到深拷贝(deep copy)的操作。也就是说,会复制字符数组中的数据,而不是仅仅复制指针或引用。这样做是为了保证每个 std::string 对象都有自己独立的字符数据存储空间,互不影响。

然而,现代 C++ 标准库的实现可能会使用一种称为“写时复制”(Copy-On-Write, COW)的技术来优化复制和赋值操作。这种技术的基本思想是,当复制或赋值一个 std::string 对象时,并不立即复制字符数据,而是让多个对象共享同一个字符数组。只有当其中一个对象被修改时,才会真正复制字符数据。这种优化可以减少不必要的内存分配和复制操作,提高性能。但需要注意的是,在多线程环境下使用 COW 技术可能会导致竞态条件和数据不一致的问题,因此现代 C++ 标准库的实现可能会避免使用这种技术,或者在多线程环境下禁用它。

注意事项

虽然 std::string 的底层实现原理对于大多数应用程序来说是透明的,但了解这些原理可以帮助你更好地理解和优化使用 std::string 的代码。例如,避免频繁地重新分配内存空间可以提高性能;使用 reserve() 函数预先分配足够的内存空间可以减少内存分配的次数;在需要频繁修改字符串的情况下,使用 std::stringbuilder(C++20 中引入)可以更有效地构建字符串。

 
 

#include <iostream>

#include <cstring>

#include <algorithm>

class String {

private:

char* str;

public:

// 构造函数

String(const char* s = "") {

str = new char[std::strlen(s) + 1];

std::strcpy(str, s);

}

// 析构函数

~String() {

delete[] str;

}

// 拷贝构造函数

String(const String& other) {

str = new char[std::strlen(other.str) + 1];

std::strcpy(str, other.str);

}

// 赋值运算符重载

String& operator=(const String& other) {

if (this != &other) {

delete[] str;

str = new char[std::strlen(other.str) + 1];

std::strcpy(str, other.str);

}

return *this;

}

// 获取字符串

const char* c_str() const {

return str;

}

};

int main() {

String s1("Hello");

String s2 = s1; // 拷贝构造

String s3;

s3 = s1; // 赋值操作

std::cout << "s1: " << s1.c_str() << std::endl;

std::cout << "s2: " << s2.c_str() << std::endl;

std::cout << "s3: " << s3.c_str() << std::endl;

return 0;

}

这段代码展示了如何用C++模拟实现一个简单的String类,包括构造函数、析构函数、拷贝构造函数和赋值运算符重载。它演示了如何分配和释放内存以管理字符串,并且如何实现浅拷贝和深拷贝。这对于理解C++中对象的创建和销毁过程,以及内存管理非常有帮助。

```python
class BertPooler(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.dense = nn.Linear(config.hidden_size, config.hidden_size)
        self.activation = nn.Tanh()

    def forward(self, hidden_states):
        # We "pool" the model by simply taking the hidden state corresponding
        # to the first token.
        first_token_tensor = hidden_states[:, 0]
        pooled_output = self.dense(first_token_tensor)
        pooled_output = self.activation(pooled_output)
        return pooled_output
from transformers.models.bert.configuration_bert import *
import torch
config = BertConfig.from_pretrained("bert-base-uncased")
bert_pooler = BertPooler(config=config)
print("input to bert pooler size: {}".format(config.hidden_size))
batch_size = 1
seq_len = 2
hidden_size = 768
x = torch.rand(batch_size, seq_len, hidden_size)
y = bert_pooler(x)
print(y.size())
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值