【c++】std::string、std::string_view 与 C-style 字符串,以及优先使用string_view

C++ 字符串类型解析: std::stringstd::string_view 与 C-style 字符串的关系

  1. C-style 字符串 (const char*):传统的字符数组,通过 \0 结尾来表示字符串。
  2. std::string:C++ 标准库提供的字符串类,封装了字符数组并提供丰富的字符串操作。
  3. std::string_view:C++17 引入的字符串视图,轻量级,不拥有数据,只是对现有字符串数据的引用。

1. C-style 字符串 (const char*)

C-style 字符串是最基础的字符串表示方式,通常通过 const char* 来表示。
例如:

const char* c_str = "Hello, World!";

C-style 字符串的特点是它没有内建的内存管理和丰富的操作方法,通常需要手动管理内存。
适用于与 C 语言兼容的函数,或者在性能要求非常高的场合。

2. std::string — C++ 标准库字符串

std::string 是 C++ 中常用的字符串类型,它封装了 C-style 字符串,提供了丰富的操作方法。
例如:

std::string str = "Hello, World!";
str += " Welcome to C++!";
std::cout << str.size();  // 输出字符串长度

std::string 自动管理内存,提供了方便的字符串操作方法,如拼接、查找、替换等。
它的缺点是可能带来一定的性能开销,特别是在频繁的内存分配和复制的场合。

3. std::string_view — 字符串视图

std::string_view 是 C++17 引入的轻量级字符串视图,它只是对现有字符串数据的引用,不拥有数据。
例如:

std::string_view view = "Hello, World!";

std::string_view 适用于需要性能优化,避免不必要的内存拷贝的场合。它非常轻量,但需要确保它所引用的数据在视图使用期间有效。


三者之间的转换关系

  1. const char* 转换为 std::string
    你可以直接使用 std::string 的构造函数或赋值操作来完成。
const char* c_str = "Hello, World!";
std::string str(c_str);

或者:

std::string str = c_str;

std::string 会复制 const char* 指向的数据,因此它会拥有该数据的副本。

  1. std::string 转换为 std::string_view
    std::string_view 不拥有数据,而是引用已有数据。你可以通过构造函数将 std::string 转换为 std::string_view
std::string str = "Hello, World!";
std::string_view view(str);

注意,std::string_view 引用的数据需要在 std::string_view 使用期间有效。

  1. std::string_view 转换为 std::string
    如果你需要一个拥有数据的 std::string,可以将 std::string_view 转换为 std::string
std::string_view view = "Hello, World!";
std::string str(view);
  1. const char* 转换为 std::string_view
    可以直接通过 std::string_view 的构造函数将 const char* 转换为视图。
const char* c_str = "Hello, World!";
std::string_view view(c_str);
  1. std::string_view 转换为 const char*
    你可以通过 std::string_viewdata() 方法获取原始的 C-style 字符串指针。
std::string_view view = "Hello, World!";
const char* c_str = view.data();

总结

在 C++ 中,选择正确的字符串类型和合理的转换方式,不仅能提升程序的性能,也能减少内存管理的复杂性。理解这三者之间的关系是高效编写 C++ 代码的基础。

  1. std::string 适用于需要对字符串进行修改和动态内存管理时。
  2. std::string_view 适用于避免不必要的字符串拷贝,提升性能的场合。
  3. const char* 用于与 C 语言兼容,或在性能要求极高的情况下使用。

C++ 中优先使用 std::string_view(按值传递)而不是 const std::string&

  1. 使用 const std::string&(会发生拷贝)
    当我们使用 const std::string& 作为函数参数时,传递给函数的字符串会进行拷贝,尤其在传递临时字符串字面量时。
  • 对于非临时对象(如 std::string large_str = “Hello”;):const std::string& 会避免拷贝,只是传递该对象的引用,因此没有发生拷贝。

  • 对于临时对象(如字符串字面量 “Hello”):编译器会将临时字符串字面量转换为一个 std::string 对象,然后传递其引用。这时,虽然是传引用,但由于 std::string 对象本身是临时创建的,仍然需要将临时对象创建并传递。因此,临时对象的创建本身涉及到一次拷贝。因为字面量本身不是 std::string 对象,而是一个字符数组。
    示例代码:

#include <iostream>
#include <string>

void print_string(const std::string& str) {
    std::cout << "String: " << str << std::endl;
}

int main() {
    std::string large_str = "Hello, this is a large string that will be passed to a function!";

    // 使用 const std::string& 参数传递
    print_string(large_str); 
    print_string("Temporary string literal"); // 这里也会发生拷贝

    return 0;
}
  1. 使用 std::string_view(避免拷贝)
    使用 std::string_view 可以避免不必要的内存拷贝,std::string_view 不会拷贝数据,它只会引用现有的字符串数据。
    示例代码:
#include <iostream>
#include <string_view>  // 需要包含 string_view 头文件

void print_string(std::string_view str_view) {
    std::cout << "String: " << str_view << std::endl;
}

int main() {
    std::string large_str = "Hello, this is a large string that will be passed to a function!";

    // 使用 std::string_view 参数传递
    print_string(large_str); // 这里没有拷贝,仅仅引用 large_str 的数据
    print_string("Temporary string literal"); // 这里没有拷贝,直接引用字面量数据

    return 0;
}

性能差异:

  • 使用 std::string_view 避免了不必要的内存拷贝,尤其是在传递临时字符串字面量时。
  • 相比之下,const std::string& 在传入字符串字面量时,会导致 std::string 对象的拷贝。
  • 对于大字符串,std::string_view 可以显著提高性能。

何时不使用 std::string_view

  • 当你需要修改字符串内容时,std::string_view 不能修改数据。
  • 需要保证 std::string_view 引用的字符串在视图生命周期内有效,避免悬空引用。

总结:
使用 std::string_view 可以提升性能,避免内存拷贝,特别是传递临时字符串字面量和大型字符串时。
但要注意它的生命周期管理问题,在合适的场景下选择使用它。


使用string_view产生悬垂引用案例

#include <iostream>
#include <string>
#include <string_view>

std::string_view askForName()
{
    std::cout << "What's your name?\n";

    // 使用 std::string
    std::string name{};
    std::cin >> name;
    
    std::string_view view{name};

    std::cout << "Hello " << view << '\n';

    // 返回 view 时,name 会销毁,导致悬垂引用
    return view;
}

int main()
{
    // `askForName` 返回一个悬垂引用
    std::string_view view{askForName()};

    // `view` 现在指向已经销毁的 `name` 字符串
    // 访问它会导致未定义行为
    std::cout << "Your name is " << view << '\n'; // 未定义行为:悬垂引用

    return 0;
}
  • askForName() 函数:
  • askForName 函数内部创建了一个局部变量 name,它存储用户输入的名字。
  • 然后,name 被转换成 std::string_view 类型的 view,并返回。
  • 然而,当 askForName 函数返回时,name 变量会离开作用域并销毁。
  • 此时,view 仍然持有对 name 字符串数据的引用。产生悬垂引用
### 回答问题 #### 1. 使用 `std::string_view` 改进之前的代码 `std::string_view` 是 C++17 引入的一个轻量级字符串视图类,它允许我们以只读的方式访问字符串数据,而无需复制底层数据。这可以显著提高性能,尤其是在处理大量字符串时。 以下是使用 `std::string_view` 改进后的代码: ```cpp #include <iostream> #include <string> #include <string_view> // 基类:Publication class Publication { protected: std::string_view title; // 使用 string_view std::string_view publisher; // 使用 string_view double price; public: // 构造函数 Publication(std::string_view t = "", std::string_view p = "", double pr = 0.0) : title(t), publisher(p), price(pr) {} // 获取方法 std::string_view getTitle() const { return title; } std::string_view getPublisher() const { return publisher; } double getPrice() const { return price; } }; // 派生类:Book class Book : public Publication { private: std::string_view ISBN; // 使用 string_view std::string_view author; // 使用 string_view public: // 构造函数 Book(std::string_view t = "", std::string_view p = "", double pr = 0.0, std::string_view is = "", std::string_view a = "") : Publication(t, p, pr), ISBN(is), author(a) {} // 获取方法 std::string_view getISBN() const { return ISBN; } std::string_view getAuthor() const { return author; } }; // 派生类:Magazine class Magazine : public Publication { private: char frequency; std::string_view editor; // 使用 string_view public: // 构造函数 Magazine(std::string_view t = "", std::string_view p = "", double pr = 0.0, char f = 'M', std::string_view e = "") : Publication(t, p, pr), frequency(f), editor(e) {} // 获取方法 char getFrequency() const { return frequency; } std::string_view getEditor() const { return editor; } }; ``` **解释:** - 使用了 `std::string_view` 替代 `std::string`,避免了不必要的字符串拷贝。 - `std::string_view` 提供了对字符串的只读访问,因此在需要频繁传递字符串的情况下,性能会有显著提升。 - 注意:`std::string_view` 的生命周期依赖于其引用的原始字符串,因此在设计时需要确保原始字符串在 `std::string_view` 使用期间不会被销毁。 --- #### 2. 使用 `std::string_view` 改进 `Library_Book` 类 以下是改进后的 `Library_Book` 类实现: ```cpp #include <iostream> #include <string> #include <string_view> // 图书馆书籍类:Library_Book class Library_Book : public Book { private: int libraryNumber; // 图书馆用户的六位编号 std::string_view borrowDate; // 使用 string_view public: // 构造函数 Library_Book(std::string_view t = "", std::string_view p = "", double pr = 0.0, std::string_view is = "", std::string_view a = "", int ln = 0, std::string_view bd = "") : Book(t, p, pr, is, a), libraryNumber(ln), borrowDate(bd) {} // 获取方法 int getLibraryNumber() const { return libraryNumber; } std::string_view getBorrowDate() const { return borrowDate; } // 打印借阅信息 void printBorrowDetails() const { std::cout << "Title: " << getTitle() << std::endl; std::cout << "Author: " << getAuthor() << std::endl; std::cout << "Library Number: " << libraryNumber << std::endl; std::cout << "Borrow Date: " << borrowDate << std::endl; } }; ``` **解释:** - 在 `Library_Book` 中,`borrowDate` 被替换为 `std::string_view`,以减少不必要的字符串拷贝。 - 其他部分保持不变,但通过使用 `std::string_view`,我们可以更高效地处理字符串数据。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值