C++ Primer阅读心得(第十七章)

2022/3/19 1:28:08

本文主要是介绍C++ Primer阅读心得(第十七章),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

C++ Primer阅读心得(第十七章)

  1. c++11标准库中新增了临时数据组合的tuple类型(python万岁!)。类似pair,我们可以通过make_tuple创造一个tuple,通过get获得其中的一个元素,通过tuple_size获得tuple中的元素个数,通过tuple_element获得tuple中指定元素的类型。tuple支持==、!=和<这几个关系运算符,比较时先看元素个数和类型是否匹配,然后再从左到右依次比较各个元素。
//创造一个tuple<int, const char*, double>
auto example = std::make_tuple(1, "tuple", 20.00); 
auto first = std::get<0>(example); //first是int型,等于1
auto len = std::tuple_size<decltype(example)>::value; //tuple长度为3
std::tuple_element<0, decltype(example)>::type x; //x是int
auto example2 = std::make_tuple(1, "tuple", 20.00);
bool is_equal = (example == example2); // true
bool less_than = (example < example2); // false

tuple最有用的地方是让一个函数同时返回多个值,就像python和golang里面那样,这在c++17提供了structured binding功能之后就非常像了。

constexpr int kSuccess = 0;
constexpr int kError = -1;
auto func_return_tuple() {
    int result = 2;
    return std::make_tuple(kSuccess, result);
}
auto [err_code, result] = func_return_tuple(); //使用structured binding直接解开tuple
  1. 标准库中提供了bitset来协助二进制集合处理(bitmap?)。
std::bitset<32> b1(0xff); //从一个大整型初始化,不足的高位填0,超出则截断
std::bitset<32> b2("1100"); //从一个01的字符串初始化
b1.any(); //检查是否至少有一个bit为1
b1.all(); //检查是否所有bit都为1
b1.none(); //检查是否所有bit都为0
b1.set(0); //0位置1,不加参数的set调用全体置1
b1.reset(0); //0位置0,不加参数的reset调用全体置0
b1.flip(0); //0位置翻转,不加参数的flip调用全体置翻转
b1[0] = 1; //下标访问
b1[0].flip(); //下标访问+翻转
b1.to_ulong(); //转成unsigned long,还有to_ullong转成unsigned long long
b1.to_string(); //转成字符串

在我的mac上实测,bitset最多支持到32M个bit,想用它解决著名的int32_t的bitmap问题的少侠们要失望了,得多来一些组合起来才行。

  1. C++11中新增了正则表达式的库regex(终于有了,泪奔!),基本使用方法是:
  • 先通过正则表达式的string(或者const char*)初始化一个regex
  • 通过regex_search来查找子串或者regex_match来整体匹配,结果会被保留在smatch(string专用,const char*对应着cmatch)中
  • 根据smatch做下一步的处理
std::string pattern = "[[:alpha:]][^c]ei[[:alpha:]]"; //匹配一个包含ei,但是不包含cei的单词
std::regex r(pattern); //初始化
std::smatch results;   //结果放这里
std::string test_str = "receipt friend theif receive"; //目标串
if (regex_search(test_str, results, r)) { //查找子串匹配
    std::cout << results.str() << std::endl; //利用查找结果
}
  1. Regex初始化时要注意:
    • 正则串默认是ECMAScript的标准,你可以通过其构造函数第二个参数指定为posix标准/扩展、awk、grep和egrep的标准
    • c++语言中反斜杠\本身就是一个特殊字符,字符串中使用的时候需要使用反斜杠转义一下,因此对于正则串中本来就需要一个反斜杠的(例如:{d}),c++中要加两个反斜杠才对(变成:\{d})
    • 正则表达式在代码执行到它的时候才编译,因此要把regex的定义尽可能的向公共方向抽取(全局变量、class member等),尤其注意不要放在循环内部
  2. Regex_search只能匹配第一个子串,为了遍历所有匹配子串,regex库提供了迭代器sregex_iterator。
std::sregex_iterator it(test_str.begin(), test_str.end(), r);
std::sregex_iterator end_it;
for (; it != end_it; ++it) { //遍历所有结果
    std::cout << it->str() << std::endl;
}
  1. 匹配结果smatch不仅可以通过str获取匹配结果,还可以获取匹配的prefix和suffix,如果正则串中含有子表达式,则可以通过下标操作获取子串匹配结果ssub_match。
results.prefix(); //拿前缀
results.suffix(); //拿后缀
std::string pattern2 = "([[:alpha:]])([^c]ei)([[:alpha:]])"; //匹配一个包含ei,但是不包含cei的单词
std::regex r2(pattern2); //初始化
std::smatch results2;   //结果放这里
if (regex_search(test_str, results2, r2)) { //查找子串匹配
    std::cout << results2[0].str() << std::endl; //打印整体匹配结果
    std::cout << results2[1].str() << std::endl; //打印第一个匹配结果
    std::cout << results2[2].str() << std::endl; //打印第二个匹配结果
    std::cout << results2[3].str() << std::endl; //打印第三个匹配结果
}
  1. 可以使用regex_replace起到查找+替换所有匹配子串的作用(类似sed和awk)
string fmt = "$1-$2-$3";
std::cout << regex_replace(test_str, r2, fmt) << std::endl; //结果为:receipt f-rei-nd t-hei-f receive
  1. C++11新增了随机发生器的库(终于可以和rand以及各种随机黑魔法say goodbye了!),随机发生器由两部分组成:随机数引擎和随机分布类,使用时先初始化一个随机数引擎(为了增强随机性,通常使用随机设备random_device产生的真随机数作为种子初始化它),然后在使用该引擎初始化你想要的随机分布,最后再调用该随机分布获取随机数。
std::random_device rd; //使用随机设备生成值作为种子
std::default_random_engine e(rd()); //默认随机引擎,推荐使用std::mt19937
std::uniform_int_distribution<int> d(0, 9); //生成一个0~9的均匀随机分布
for(int i = 0; i < 10; ++i) {
    std::cout << d(e) << std::endl; //使用引擎调用随机分布获得随机数
}
  1. 使用随机生成器时要注意到一点,为了程序调试的方便,每次创建一个新的生成器之后,其所返回的序列都是一样的,所以不要把随机生成器的初始化放在循环、子函数中。在Release版本中,我们可以通过将其static化或者设置random_device产生的随机seed来打破这一点。
  2. 随机生成器支持多种分布,包括:正态分布、二项分布等等,可以根据实际需要选择合适的分布。
  3. 格式化输入输出:可以通过操纵符来修改iostream的的格式状态,实现格式化输入输出(另有乾坤啊,我只用过boolalpha)。大多数格式操纵符都是成对出现的,一个负责设置,另外一个负责复原。
bool b = true;
std::cout << std::boolalpha << b << std::noboolalpha << std::endl; //输出true而不是1

更多的格式操纵符还有:

名称作用相反操作
boolalphabool值显示为true和falsenoboolalpha
showbase显示进制noshowbase
showpoint总是显示小数点noshowpoint
dec/hex/oct10进制/16进制/8进制dec/hex/oct
skipws输入跳过空白noskipws
  1. 为格式化输入输出:标准库还提供了一组输入输出的低层操作,将输入输出流当作无解释的字节序列来处理,从而提供更加定制化的能力。
char ch;
std::cin.get(ch);  //从cin中读取一个字符
std::cout.put(ch); //向cout中输出一个字符
char buffer[10] = {};
std::cin.get(buffer, 10, '\n');      //读10个字符上来,或者遇到回车截止
std::cin.getline(buffer, 10, '\n');  //读一行上来,或者10个或者遇到回车截止
std::cout.write(buffer, 10);         //输出10个字符
  1. fstream和strstream这两个流支持随机访问,使用seek去到想要的offset,使用tell获取当前的offset。Seek和tell区分输入和输出,以g结尾的seekg和tellg是输入流(读取)用的,以p结尾的seekp和tellp是输出流(写入)用的。注意:输入和输出流共用一个缓冲区和一个offset标记,不存在单独的读offset和写offset,因此读写之后一定要seekg/seekp到指定offset才能继续操作。


这篇关于C++ Primer阅读心得(第十七章)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程