LearnCpp5.10 – stdstring_view 简介

5.10 – std::string_view 简介

请考虑以下程序:

#include <iostream>

int main()
{
    int x { 5 }; // x makes a copy of its initializer
    std::cout << x << '\n';

    return 0;
}

执行 x 的定义时,初始化值 5 被复制到为变量 int x 分配的内存中。对于基本类型,初始化和复制变量的速度很快。

现在考虑这个类似的程序:

#include <iostream>
#include <string>

int main()
{
    std::string s{ "Hello, world!" }; // s makes a copy of its initializer
    std::cout << s << '\n';

    return 0;
}

初始化 s 时,C 风格字符串文本 “Hello, world!” 将复制到为 string s 分配的内存中。与基本类型不同,初始化和复制 string 很慢。

在上面的程序中,我们对 s 所做的只是将值打印到控制台,然后 s 被销毁。我们基本上已经制作了一份 “Hello, world!” 的副本,只是为了打印然后销毁该副本。这效率很低。

在这个例子中,我们看到类似的内容:

#include <iostream>
#include <string>

void printString(std::string str) // str makes a copy of its initializer
{
    std::cout << str << '\n';
}

int main()
{
    std::string s{ "Hello, world!" }; // s makes a copy of its initializer
    printString(s);

    return 0;
}

此示例制作了两个 C f风格字符串 “Hello, world!” 的副本:一个是在 main() 中初始化 s,另一个是在 printString() 中初始化参数 str 时。光是打印一个字符串就有很多不必要的复制

std::string_view C++17

为了解决 std::string 初始化(或复制)成本高昂的问题,C++17 引入了 std::string_view(头文件)。std::string_view 提供对现有字符串(C 样式字符串、std::stringstd::string_view)的只读访问,而无需创建副本。只读意味着我们可以访问和使用正在查看的值,但不能修改它。

#include <iostream>
#include <string_view> // C++17

// str provides read-only access to whatever argument is passed in
void printSV(std::string_view str) // now a std::string_view
{
    std::cout << str << '\n';
}

int main()
{
    std::string_view s{ "Hello, world!" }; // now a std::string_view
    printSV(s);

    return 0;
}

此程序生成与前一个程序相同的输出,但不会生成字符串 “Hello, world!” 的副本。

当我们使用 C 风格字符串文字 “Hello, world!” 初始化 std::string_view s 时,s 提供对 “Hello, world!”的只读访问,而无需复制字符串。当我们将 s 传递给 printSV() 时,参数 strs 初始化。这允许我们通过 str 访问 “Hello, world!”,同样不需要复制字符串。

使用std::string_view在这种情况下确实有其意义,尽管在这个简单的例子中可能不是很明显。以下是一些使用std::string_view的好处:

  1. 性能提升:由于std::string_view不复制字符串,它只是引用现有的字符数组,因此在传递大型字符串时可以减少内存使用和避免不必要的复制操作,从而提高性能。
  2. 接口灵活性printSV函数现在可以接受任何形式的字符串,包括C风格字符串(const char*)、std::string对象以及其他的std::string_view对象,而无需进行任何转换或复制。
  3. 明确意图:通过使用std::string_view,你向函数的使用者传达了一个明确的信号,即这个函数不会修改传入的字符串。这是一个很好的API设计实践,因为它提高了代码的可读性和可维护性。

Best practice 最佳实践
当您需要只读字符串时,尤其是对于函数参数时,首选 std::string_view 而不是 std::string

std::string_view 可以使用许多不同类型的字符串进行初始化

std::string_view 的一大优点是它的灵活性。std::string_view 对象可以使用 C 风格字符串、std::string 或其他 std::string_view 进行初始化:

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

int main()
{
    std::string_view s1 { "Hello, world!" }; // initialize with C-style string literal
    std::cout << s1 << '\n';

    std::string s{ "Hello, world!" };
    std::string_view s2 { s };  // initialize with std::string
    std::cout << s2 << '\n';

    std::string_view s3 { s2 }; // initialize with std::string_view
    std::cout << s3 << '\n';
       
    return 0;
}

std::string_view 参数将接受许多不同类型的字符串参数

C 样式字符串和 std::string 都将隐式转换为 std::string_view。因此,std::string_view 参数将接受 C 样式字符串、std::stringstd::string_view 类型的参数:

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

void printSV(std::string_view str)
{
    std::cout << str << '\n';
}

int main()
{
    printSV("Hello, world!"); // call with C-style string literal

    std::string s2{ "Hello, world!" };
    printSV(s2); // call with std::string

    std::string_view s3 { s2 };
    printSV(s3); // call with std::string_view
       
    return 0;
}

std::string_view不会隐式转换为std::string

因为 std::string 会复制其初始值设定项(这很昂贵),所以 C++ 不允许将 std::string_view 隐式转换为 std::string。这是为了防止意外地将 std::string_view 参数传递给 std::string 参数,并在可能不需要此类副本的地方无意中制作昂贵的副本。

但是,如果需要,我们有两个选择:

  1. 使用 std::string_view 初始化器显式创建 std::string(这是允许的,因为很少会无意中这样做
  2. 使用 static_cast 将现有的 std::string_view 转换为 std::string

以下示例显示了这两个选项:

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

void printString(std::string str)
{
	std::cout << str << '\n';
}

int main()
{
	std::string_view sv{ "Hello, world!" };

	// printString(sv);   // compile error: won't implicitly convert std::string_view to a std::string

	std::string s{ sv }; // okay: we can create std::string using std::string_view initializer
	printString(s);      // and call the function with the std::string

	printString(static_cast<std::string>(sv)); // okay: we can explicitly cast a std::string_view to a std::string

	return 0;
}

赋值会改变 std::string_view 当前查看的内容

以下示例对此进行了说明:

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

int main()
{
    std::string name { "Alex" };
    std::string_view sv { name }; // sv is now viewing name
    std::cout << sv << '\n'; // prints Alex

    sv = "John"; // sv is now viewing "John" (does not change name)
    std::cout << sv << '\n'; // prints John

    std::cout << name << '\n'; // prints Alex

    return 0;
}

在上面的示例中,sv = “John” 会导致 sv 现在查看字符串 “John”。它不会更改 name 保存的值(仍为 “Alex”)。

std::string_view 的文本

默认情况下,双引号字符串文本是 C 样式的字符串文本。我们可以通过在双引号字符串文本后使用 sv 后缀来创建类型为 std::string_view 的字符串文本。sv 必须为小写。

#include <iostream>
#include <string>      // for std::string
#include <string_view> // for std::string_view

int main()
{
    using namespace std::string_literals;      // access the s suffix
    using namespace std::string_view_literals; // access the sv suffix

    std::cout << "foo\n";   // no suffix is a C-style string literal
    std::cout << "goo\n"s;  // s suffix is a std::string literal
    std::cout << "moo\n"sv; // sv suffix is a std::string_view literal

    return 0;
}

可以使用 C 样式字符串文本初始化 std::string_view 对象(无需使用 std::string_view 文本初始化它)。

也就是说,使用 std::string_view 字面值初始化 std::string_view 不会引起问题(因为这样的字面值实际上是伪装的 C 样式字符串字面值)。

constexpr std::string_view

std::string 不同,std::string_view 完全支持 constexpr(它允许在编译时对变量进行求值和初始化):

#include <iostream>
#include <string_view>

int main()
{
    constexpr std::string_view s{ "Hello, world!" }; // s is a string symbolic constant
    std::cout << s << '\n'; // s will be replaced with "Hello, world!" at compile-time

    return 0;
}

这使得 constexpr std::string_view 成为需要字符串符号常量时的首选。

我们将在下一课中继续讨论 std::string_view

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值