C++编程:使用std::string仿写c++17的std::string_view
·
0. 引言
在性能敏感的应用场景中,如网络库和系统编程,频繁的 std::string 字符串拷贝会导致显著的性能开销。C++17 引入的 std::string_view 能有效减少不必要的拷贝操作。然而,由于项目要求只能使用 C++14,因此我们需要自己实现一个类似的 StringView 类来代替 std::string_view,以优化性能。
本文将基于 C++11 实现 StringView 类,参考开源库 muduo 的 StringPiece.h,以避免不必要的内存分配和字符串拷贝。
适用场景:
- 高效处理大文本(如日志分析)。
- 频繁传递字符串,但不需要修改内容。
- 替代 C++17 的
std::string_view。
1. 完整代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <string>
class StringView {
private:
const char* data_ = nullptr; // 指向外部字符串
std::size_t size_ = 0; // 字符串长度
static constexpr std::size_t npos = static_cast<std::size_t>(-1); // 定义 npos
public:
StringView() noexcept = default;
explicit StringView(const char* str) : data_(str), size_(str ? std::strlen(str) : 0) {}
StringView(const char* str, std::size_t len) : data_(str), size_(str ? len : 0) {}
explicit StringView(const std::string& str) noexcept : data_(str.data()), size_(str.size()) {}
std::size_t size() const noexcept {
return size_;
}
bool empty() const noexcept {
return size_ == 0;
}
const char* data() const noexcept {
return data_;
}
const char& operator[](std::size_t index) const {
if (!data_ || index >= size_) {
throw std::out_of_range("Index out of range");
}
return data_[index];
}
StringView substr(std::size_t pos, std::size_t len = npos) const {
if (pos > size_) {
throw std::out_of_range("Position out of range");
}
len = std::min(len, size_ - pos);
return StringView(data_ + pos, len);
}
int compare(const StringView& other) const noexcept {
std::size_t len = std::min(size_, other.size_);
int result = std::memcmp(data_, other.data_, len);
return result != 0 ? result : static_cast<int>(size_ - other.size_);
}
bool starts_with(const StringView& prefix) const noexcept {
return size_ >= prefix.size_ && std::memcmp(data_, prefix.data_, prefix.size_) == 0;
}
bool ends_with(const StringView& suffix) const noexcept {
return size_ >= suffix.size_ && std::memcmp(data_ + size_ - suffix.size_, suffix.data_, suffix.size_) == 0;
}
bool contains(const StringView& sub) const noexcept {
if (sub.size_ > size_) return false;
for (std::size_t i = 0; i <= size_ - sub.size_; ++i) {
if (std::memcmp(data_ + i, sub.data_, sub.size_) == 0) {
return true;
}
}
return false;
}
friend std::ostream& operator<<(std::ostream& os, const StringView& sv) {
if (sv.data_ && sv.size_ > 0) {
os.write(sv.data_, sv.size_);
}
return os;
}
bool operator==(const StringView& other) const noexcept {
return size_ == other.size_ && compare(other) == 0;
}
bool operator!=(const StringView& other) const noexcept {
return !(*this == other);
}
bool operator<(const StringView& other) const noexcept {
return compare(other) < 0;
}
bool operator<=(const StringView& other) const noexcept {
return compare(other) <= 0;
}
bool operator>(const StringView& other) const noexcept {
return compare(other) > 0;
}
bool operator>=(const StringView& other) const noexcept {
return compare(other) >= 0;
}
};
2. 实现原理
数据成员:
data_:指向外部字符串的指针。size_:表示字符串长度,避免每次访问时重复调用std::strlen。
构造函数:
- 支持从 C 风格字符串、指定长度、或
std::string构造。 - 对空指针(
nullptr)进行处理,确保安全。
基本操作:
- 提供
substr方法,允许创建子字符串视图,并进行边界检查以避免越界。 - 提供
starts_with、ends_with和contains方法,用于高效地检查前缀、后缀和子串。
性能优化:
- 字符串比较通过
std::memcmp进行,支持按字节比较子字符串。 operator<<直接输出字符串内容,避免额外的内存拷贝。
安全性:
- 处理了空指针、越界访问等潜在问题,确保符合 C++ 标准的异常安全性。
3. 测试程序
以下是测试 StringView 类的示例程序,涵盖构造、访问、子串操作、比较等功能。
int main() {
const char* text = "Hello, world!";
StringView sv1(text); // 整个字符串
StringView sv2(text, 5); // 前 5 个字符
std::string str = "Hello, OpenAI!";
StringView sv3(str); // 从 std::string 构造
std::cout << "sv1: " << sv1 << ", size: " << sv1.size() << "\n";
std::cout << "sv2: " << sv2 << ", size: " << sv2.size() << "\n";
std::cout << "sv3: " << sv3 << ", size: " << sv3.size() << "\n";
std::cout << "sv1 starts with 'Hello': " << sv1.starts_with(StringView("Hello")) << "\n";
std::cout << "sv1 ends with 'world!': " << sv1.ends_with(StringView("world!")) << "\n";
std::cout << "sv1 contains 'world': " << sv1.contains(StringView("world")) << "\n";
try {
auto sv4 = sv1.substr(7, 5);
std::cout << "sv4 (substr): " << sv4 << "\n";
} catch (const std::out_of_range& e) {
std::cout << "Error: " << e.what() << "\n";
}
std::cout << "Comparison (sv1 vs sv2): " << (sv1 == sv2) << "\n";
return 0;
}
4. 输出结果
运行上述程序时,输出如下:
sv1: Hello, world!, size: 13
sv2: Hello, size: 5
sv3: Hello, OpenAI!, size: 14
sv1 starts with 'Hello': 1
sv1 ends with 'world!': 1
sv1 contains 'world': 1
sv4 (substr): world
Comparison (sv1 vs sv2): 0
通过此实现,我们能够有效避免字符串拷贝带来的性能问题,并且在不引入额外内存分配的情况下,提供灵活和高效的字符串视图操作。
更多推荐
所有评论(0)