ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++学习笔记(九)—— 进阶内容

2022-01-17 01:01:48  阅读:150  来源: 互联网

标签:regex 匹配 进阶 int pos 笔记 c++ error 函数


特殊库

tuple

模糊获取tuple的成员数量和类型

typedef decltype(item) trans;
// 获取成员数量
size_t sz = tuple_size(trans)::value;
// cnt和item中的第二个成员类型相同
tuple_element<1, trans>::type cnt = get<1>(item);

bitset

头文件:bitset

构造函数

原型 说明 说明2 样例
bitset b; n位,每一位均为0 constexpr
bitset b(u); unsigned long long低n位的拷贝 constexpr
bitset b(s, pos, m, zero, one); 从pos开始m个字符的拷贝。0和1的字符支持自定义
bitset b(cp, pos, m, zero, one); 和上一个一样,换成char*

操作

置位:x位变成1
复位:x位变成0

原型 说明
b.any() 是否存在置位
b.all() 是否所有位都置位
b.none() 不存在置位?
b.count() 置位的位数
b.size() constexpr,返回位数
b.test(pos) 如果pos是置位的则为true,否则为false
b.set(pos, v) 设置。v默认为true
b.set() 将b中所有位置位
b.reset(pos) pos位置复位
b.reset() 所有位复位
b.flip(pos) 反转pos位
~b[pos] 和上一个等价
b.flip() 反转所有位
b[pos] 如果b是const,则返回bool
b.to_ulong() 拼成unsigned long。如果放不下,抛出overflow_error异常
b.to_ullong() 同上,拼成unsined long long
b.to_string(zero, one) 拼成string。zero和one默认为'0'和'1'
os << b 打印0和1的字符串
is >> b 当下一位不是0或1,或者已经读满时,读取停止

正则表达式

头文件:regex
默认情况下regex的正则语言是:ECMAScript(ECMA-262规范)

搜索方法

  • regex_search:如果输入序列的一个子串和表达式匹配,返回true
  • regex_match:如果整个序列都匹配,返回true

这俩接口的参数是一样的:

(seq, m, r, mft)
(seq, r, mft)

seq: 匹配串
r: 正则串,regex对象
m:match容器,用于存储结果
mft:匹配参数,详细见后

regex

  • regex r(re, f=ECMAScript)
    re表示一个正则表达式,f表示标志,详细见下
  • r1 = re
    regex支持直接赋值
  • r1.assign(re, f)
    和上面等价
  • r.mark_count()
    r中表达式的数目
  • r.flags
    返回r的标志集

标志集

定义在regex和regex_constants::syntax_option_type中

  • icase: 在匹配过程中忽略大小写
  • nosubs:不保存匹配的子表达式
  • optimize:执行速度优先于构造速度
  • ECMAScript:使用ECMA-262指定的语法
  • basic:使用POSIX基本的正则表达式语法
  • extended:使用POSIX扩展的正则表达式语法
  • awk:使用POSIX的awk语言的语法
  • grep:使用POSIX的grep语言的语法
  • egrep:使用POSIX的egrep语言的语法

匹配结果容器

  • smatch:string类型的输入序列
  • cmatch:char*
  • wsmatch:wstring
  • wcmatch:wchar*

smatch会存储n+1个结果(n表示表达式的数量),第0个表示整个匹配,后面跟上每个子表达式的结果。

异常处理

如果正则表达式错了,会抛出regex_error错误

  • error_collate:无效的元素校对请求
  • error_ctype:无效的字符类
  • error_escape:无效的转义字符或无效的尾指转义
  • error_backref:无效的向后引用
  • error_brack:不匹配的方括号
  • error_paren:不匹配的小括号
  • error_brace:不匹配的花括号
  • error_badbrace:{}中无效的范围
  • error_range:无效的字符范围,如[z-a]
  • error_space:内存不足
  • error_badrepeat:重复字符*、?、+或{之前没有有效的正则表达式
  • error_complexity:要求的匹配过于复杂
  • error_stack:栈空间不足

注意点

  1. 【.】在正则中表示所有字符,如果想匹配字面意义上的【.】的话,需要加通配符。但是【\】在正则中也是特殊字符,所以在正则中【\\】才表示去正则,即【\\.】

获取所有匹配结果

  • sregex_iterator it(b, e, r);
  • sregex_iterator end; // 尾后迭代器/空迭代器

example:

string search_s("receipt freind theif receive");
string pattern("[^c]ei");
pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*";
regex r(pattern, regex::icase);
for (sregex_iterator it(search_s.begin(), search_s.end(), r), end_it; it != end_it; ++it)
{
    cout << it->str() << endl;
}
/*
    输出结果:
    freind
    receive
*/

smatch操作

  • m.size()
    如果匹配失败,返回0;否则返回最近一次匹配的正则表达式中子表达式的数目
  • m.prefix()
    ssub_match对象,表示当前匹配之前的序列
  • m.suffix()
    ssub_match对象,表示当前匹配之后的序列
  • m.str(n)
    第n个子表达式匹配的string
  • m[n]
    对应第n个子表达式的ssub_match对象

ssub_match操作

  • matched
    是否匹配了
  • first/second
    匹配元素首尾迭代器,未匹配则两者相等
  • length()
    匹配的大小
  • str()/s = ssub
    匹配部分

regex_replace

using std::regex_constants::format_default;

string fmt = "$2.$5.$7";
regex r(phone),
string num = "(908)555-1800";
cout << regex_replace(num, r, fmt, format_default) << endl;
// 输出:908.555.1800

随机数

因为rand库存在局限性,所以c++程序员应该使用default_random_engine类和恰当的分布类对象

  • 随机数引擎类:生成随机unsigned整数序列
  • 随机数分布类:使用引擎返回服从特定概率分布的随机数

随机数发生器:分布对象和引擎对象的组合

一般使用方式:

uniform_real_distribution<double> u(0, 1);  //分布类一般为xxx_distribution,详细数据搜索相关文档
default_random_engine e; //引擎类一般为xxx_engine,详细数据搜索相关文档
cout << u(e) << endl;

IO操作符

用于控制IO如何格式化的细节,如果整型值是几进制、浮点数的精度、输出元素的宽度等

注: 当操作符改变流的格式状态时,通常改变后的状态对所有后续IO都生效。因此要注意及时还原

使用样例

//控制布尔值的格式
cout << true << " " << false << endl 
     << boolalpha
     << true << " " << false << endl;
/*
预期结果:
1 0
true false
*/

操作符清单

前面带(*)的表示默认状态

  • boolalpha: 将true和false输出为字符串
  • (*) noboolalpha: T/F输出为1/0
  • showbase:对整型值输出表示进制的前缀
  • (*) noshowbase:不输出前缀
  • showpoint:对浮点数总是显示小数点
  • (*) noshowpoint:只有当浮点值包含小数部分时才显示小数点
  • showpos:非负数显示+
  • (*) noshowpos:非负数不显示+
  • uppercase:十六进制打印0X,科学计数法打印E
  • (*) nouppercase:十六进制打印0x,科学计数法打印e
  • (*) dec:整型值显示为十进制
  • hex:整型值显示为十六进制
  • oct:整型值显示为八进制
  • left:在值的右侧添加填充字符
  • right:在值的左侧添加填充字符
  • internal:在符号和值之间添加填充字符
  • fixed:浮点数显示为定点十进制
  • scientific:浮点数显示为科学计数法
  • hexfloat:浮点数显示为十六进制(c++11)
  • defaultfloat:重置浮点数格式为十进制(c++11)
  • unitbuf:每次输出操作后都刷新缓冲区
  • (*) nounitbuf:恢复正常的缓冲区刷新方式
  • (*) skipws:输出运算符跳过空白符
  • noskipws:输出运算符不跳过空白符
  • flush:刷新ostream缓冲区
  • ends:插入空字符,刷新缓冲区
  • endl:冲入换行符,刷新缓冲区

iomanip库

  • setfill(ch):用ch填充空白
  • setprecision(n):设置精度
  • setw(w):控制读写值宽度
  • setbase(b):将输出为b进制

这些方法只控制下一个输出的状态,不改变输出流

未格式化的IO操作

单字节处理:

  • is.get(ch)
  • os.put(ch)
  • is.get()
  • is.putback(ch)
  • is.unget()
  • is.peek()

多字节处理:

  • is.get(sink, size, delim): 读size个字符,存到sink里,直到读到delim
  • is.getline(sink, size, delim)
  • is.read(sink, size)
  • is.gcount(): 返回上一个为格式化读取操作从is读取的字节数
  • os.write(source, size)
  • is.ignore(size, delim)

流随机访问

只支持fstream和sstream
下列操作g表示输入流,p表示输出流

  • tellg(): 返回当前标记的位置
  • tellp()
  • seekg(pos): 将标记重定位到绝对位置
  • seekp(pos)
  • seekg(off, from):定位到from之前或之后的off个字符
  • seekp(off, from)

读取文件样例:

ifstream oFile;
oFile.open("xxx", ios::in | ios::binary);
if (oFile)
{
  oFile.seekg(0, oFile.end);
  size_t iSize = oFile.tellg();
  char* sink = new char[iSize + 1];
  oFile.seekg(0, oFile.beg);
  oFile.read(sink, iSize);
  oFile.close();
}

用于大型程序的工具

异常处理

构造函数异常

处理构造函数初始值异常的唯一方法是将构造函数写成函数try语句块

template <typename T>
Blob<T>::Blob(std::initializer_list<T> il) try: data(std::make_shared<std::vector<T>>(il)) {
  //空函数体
} catch(const std::bad_alloc &e) {
  handle_out_of_memory(e);
}

异常类层次

  • exception
    • bad_cast
    • bad_alloc
    • runtime_error
      • overflow_error
      • underflow_error
      • range_error
    • logic_error
      • domain_error
      • invalid_argument
      • out_of_error
      • length_error

命名空间

内联命名空间

inline namespace PersonCode {
  void func1();
}
namespace PersonCode { // 隐式内联
  void func2();
}
namespace PersonCode2 {
  void func3();
}

// 另一个文件
namespace MainCode{
  #include "PersonCode.h"
  #include "PersonCode2.h"
}
MainCode::func1();  // 可以直接使用
MainCode::func2();
MainCode::PersonCode2::func3();  // 需要一层层申明

未命名的命名空间

仅在特定的文件内部有效,其作用范围不会横跨多个不同的文件。

如果两个文件都含有未命名的命名空间,则这两个空间互相无关。

命名空间别名

一个命名空间可以有好几个同义词或别名,所有别名都与命名空间原来的名字等价。

namespace person = PersonCode;

特殊工具和技术

运行时类型识别(RTTI)

有时会用在不同库指针或引用直接交互的情况

指针类型动态转换(dynamic_cast)

// base含有虚函数,Derived是base的公有派生类
if (Derived *dp = dynamic_cast<Derived*>(bp))
{
  // 使用dp指向的Derived对象
}
else
{
  // 使用bp指向的base对象
}

引用类型动态转换(dynamic_cast)

void f(const base &b)
{
  try {
    const  &d = dynamic_cast<const Derived&>(b);
    // 使用b引用的Derived对象
  }
  catch (std::bad_cast)
  {
    // 处理转换失败的情况
  }
}

typeid

用来动态判断类型的函数。

  1. 顶层const会被忽略
  2. 如果表达式是一个引用,会返回该引用所引对象的类型(即不包含&)
  3. 作用数组或函数时,不会执行向指针的类型转换

当typeid作用于指针时(而非指针所指的对象),返回的结果是该指针的静态编译时的类型

Derived* dp = new Derived();
Base* bp = dp;
cout << boolalpha;
if (typeid(*bp) == typeid(*dp))
{
    cout << "test1: " <<  true << endl;
}
if (typeid(*bp) == typeid(Derived))
{
    cout << "test2: " << true << endl;
}
cout << false << endl;
// 运行结果: false

typeid操作

返回值类型:type_info

typeid(Derived).name();  // 输出类型名,具体输出内容依赖编辑器
typeid(Derived).before(typeid(Base));  //判断前后顺序,顺序关系依赖于编辑器

使用RTTI

bool operator== (const Base &lhs, const Base &rhs)
{
  return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}

这个写法的优点:

  1. 即使是继承关系的类,也能确保不同类之间不相等
  2. 派生类中,可以重载equal

枚举

有两种:限定作用域的和不限定作用域的

  • enum:不限定
  • enum class:限定,C++11添加

不限定作用域的可以隐式转化成int,限定作用域的不可以,所以作为库的对外接口时,不能用enum class

C++11可以【设置潜在类型】和【提前声明enum】

设置潜在类型

默认类型是int

enum intValues : unsigned long long
{
  long_type = 111111111111111111111111ULL,
}

提前声明enum

enum intValues : unsigned long long;

类成员指针

可以在类的外部定义指向数据成员和函数成员的指针

class Math
{
public:
    int x, y;
    Math() = default;
    Math(int x1, int y1) : x(x1), y(y1) {};

    int GetY()
    {
        return y;
    }

    int SetAndGetY(int y2)
    {
        this->y = y2;
        return this->y;
    }

    bool SetXY(int x1, int y1)
    {
        x = x1;
        y = y1;
        return true;
    }


private:
    int z = 6;
    int GetZ()
    {
        return z;
    }
};

int main()
{
    Math obj(1, 2);

    const int Math::*p = &Math::x;
    cout << obj.*p << endl;

    auto pmf = &Math::GetY;
    cout << (obj.*pmf)() << endl;

    int (Math:: * pmf2)(int);
    pmf2 = &Math::SetAndGetY;
    cout << (obj.*pmf2)(6) << endl;
    cout << (obj.*pmf)() << endl;

    using SetXY = bool (Math::*)(int x1, int y1);
    SetXY set_xy = &Math::SetXY;
    cout << boolalpha << (obj.*set_xy)(1, 2) << endl;
    // 不可以改成obj.*set_xy(1, 2)
}

因为函数调用运算符的优先级较高,所以在声明指向成员函数的指针并使用这样的指针进行函数调用时,括号必不可少: (obj.*set_xy)(1, 2)

union

联合是一种特殊的类,一种节省空间的类。

union可以定义包含构造函数和析构函数在内的成员函数。但是因为它既不能继承其他类,也不能作为基类使用,所以union中不能含有虚函数。

匿名union

union {
  char cval;
  int ival;
};

匿名union不能包含private和protect,也不能定义成员函数

匿名的union在该作用域内可以直接访问成员,因此常用于管理类中的成员

局部类

局部类的所有成员(包括函数)都必须完整定义在类的内部。因此局部类和嵌套类的作用还是差的很远。

局部类只能访问外层作用域定义的类型名、静态变量以及枚举成员。普通局部变量无法访问。

不可移植的特性

因机器而异的特性,通常换台机器就要重写。

位域

位域在内存中的布局是与机器相关的

class File {
  unsigned int mode : 2;
}

volatile

当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为volatile,告诉编辑器不应对这样的对象进行优化。

注:合成的拷贝/移动构造函数以及赋值运算符,不能用于volatile对象,除非自定义。

链接指示:extern "C"

用于指出这是非c++函数用的语言。常用于库的接口。

在构建dll时,通常还会带上_declspec(dllexport)声明导出函数、类、对象。

尤其是函数指针,有没有指示是不同的

void (*pf1)(int);  // 指向一个c++函数

#ifdef __cplusplus  // 兼容c和c++编译同一个源文件
extern "C" void (*pf2)(int);  // 指向一个c函数
#endif

pf1 = pf2;  // 错误:类型不同

tips

  • 正则表达式是在运行时编译的,所以操作是非常慢的,注意避免多余的regex构建。尤其在循环中,尽可能在循环外创建regex
  • 循环中使用随机数需要注意seed的问题,同一个循环中的time(0)是相同的,会生成相同的结果
  • 在栈展开的过程中,运行类类型的局部对象的析构函数。因为这些析构函数是自动执行的,所以他们不应该抛出异常。一旦在栈展开的过程中析构函数抛出了异常,并且析构函数自身没能捕获到该异常,则程序将被终止。
  • 头文件最多只能在它的函数或命名空间内使用using指示或using声明

标签:regex,匹配,进阶,int,pos,笔记,c++,error,函数
来源: https://www.cnblogs.com/end-emptiness/p/15811899.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有