Skip to content
Go back

abseil C++ Tips

Edit page

Forrest Gump Fake Quote

Table of contents

Open Table of contents

Tip of the Week #1: string_view

当函数参数为(const) string时,常见的一般有3种方法,第三种方法可能不太常见。

// C Convention
void TakesCharStar(const char* s);

// Old Standard C++ convention
void TakesString(const std::string& s);

// string_view C++ conventions
void TakesStringView(absl::string_view s);    // Abseil
void TakesStringView(std::string_view s);     // C++17

前面两种方法只有当参数类型匹配时才比较方便,如果对void TakesCharStar(const char* s);函数传入std::string,需要调用c_str()函数。

void AlreadyHasString(const std::string& s) {
  TakesCharStar(s.c_str());               // explicit conversion
}

如果是对函数void TakesString(const std::string& s);传入const char*,可以直接调用,但是实际上会触发std::string的拷贝构造函数,程序效率存在问题。

void AlreadyHasCharStar(const char* s) {
  TakesString(s); // compiler will make a copy
}

What to Do ? 可以使用c++17中的sting_view或absl::string_view,string_view可以支持两种类型直接调用,虽然存在隐式类型转换,但不会拷贝整个数据,所以不存在O(n)的内存消耗。string_view只是记录了自身对应字符串的地址信息。当const char*作为参数时,隐含传入了strlen()作为string_view的构造参数。

void TakesStringView(std::string_view s);   // C++17

void AlreadyHasString(const std::string& s) {
  TakesStringView(s); // no explicit conversion; convenient!
}

void AlreadyHasCharStar(const char* s) {
  TakesStringView(s); // no copy; efficient!
}

string_view详细教程参考:learn c++ 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(C++17 引入)是一个只读的轻量级字符串视图,字符串视图并不真正的创建或者拷贝字符串,而只是拥有一个字符串的查看功能。 上面的代码演示string_view支持通过C-style string 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;
}

运行结果:

Alex
John
Alex

对string_view对象赋值,改变的是sting_view指向的字符串,并不会改变字符串本身的值。

#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;
}

运行结果:

Hello, world!
Hello, world!

C++中未提供std::string_view向std::string的隐式类型转换,这样可以阻止不小心将std::string_view隐式转换到std::string,避免拷贝带来的性能开销。

尽量以const,enum,inline替换#define

宁可以编译器替换预处理器。 例如#define ASPECT_RATIO 1.653 符号ASPECT_RATIO从未被编译器看见,在预处理阶段已经完成替换,运行时错误时如果在代码中存在多个1.653将存在困惑。

解决办法是使用const double AspectRatio = 1.653,编译后进入符号表。 为了将常量的作用域限制于class内,你必须让它成为class的一个成员,为了确保此常量至多只有一份实例,你必须使用static.

#include <iostream>
using namespace std;

class GamePlayer {
private:
    static const int NumTurns = 5;//常量声明式
    int score[NumTurns];
};

int main(){
    return 0;
}

如果需要取某个class专属常量的地址,必须使用如下方式:

#include <iostream>
using namespace std;

class GamePlayer {
public:
    static const int NumTurns = 5;
    int score[NumTurns];
};

const int GamePlayer::NumTurns;
int main(){
    cout << GamePlayer::NumTurns << ",address NumTurns:"<<&GamePlayer::NumTurns;
    return 0;
}

Edit page
Share this post on:

Previous Post
协程
Next Post
vector动态扩容存在的陷阱