目录
一.Lambda表达式
- Lambda表达式: 就是一个返回函数指针的表达式,它定义和返回值函数指针在一起的
对于函数指针来说,我们普通的写法是这样子的:
int Max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
//普通写法
int (*pMax)(int, int) = Max;
cout << pMax(1, 2) << endl;
return 0;
}
为什么要用lambda表达式?当函数体比较小,实现的功能不那么复杂的时候,用lambda表达式能使代码更为简洁,紧凑。lambda表达式怎么写呢?
[捕获方式](函数参数)mutable exception->函数返回值类型{函数体;}
我们以上面的函数来举个例子:
int main()
{
int (*pMax)(int, int) = [](int a, int b)mutable noexcept->int {return a > b ? a : b; };
//省略写法
auto ppMax = [](int a, int b) {return a > b ? a : b; };
return 0;
}
如果函数要使用外部变量,则需要捕获,捕获方式总结如下:
[=] //值的方式捕获
[&] //引用的方式捕获
[this]//this指针方式捕获,通常用在类中
[] //不捕获任何变量
[=,&x];//x用引用方式捕获,其他变量用值的方式捕获
值捕获与普通捕获的区别?
- 用值捕获时,在Lambad表达式中不能把值当成左值使用,即函数的调用不会因为值的改变而改变
int main()
{
int data = 1;
auto pFunc = [=] { cout << data << endl; }; //无参() 可以省略
auto pFunc2 = [&] { cout << data << endl; };
pFunc();
pFunc2();
data = 2;
pFunc();
pFunc2();
return 0;
}

二.仿函数
- 什么仿函数? 类模仿函数调用行为,实质是无名对象调用重载的()函数
- 所以仿函数的关键点在于重载()
- 一般情况仿函数是做排序准则,或者一些算法的计算准则
- 标准库仿函数所在头文件:<functional>
自己手写仿函数:
class Sum
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
int main()
{
//重载的()的调用方式
Sum s;
cout << "显式调用:" << s.operator()(1, 3) << endl; //显式调用重载函数
cout << "隐式调用:" << s(1, 3) << endl;
//用{}和()帮助编译器去做解析
cout << "无名调用:" << Sum{}(1, 3) << endl; //类模仿函数调用行为--->仿函数
cout << "无名调用:" << Sum()(1, 3) << endl; //类模仿函数调用行为--->仿函数
return 0;
}
标准库中的仿函数:
//算术
cout << plus<int>{}(1, 3) << endl;
//关系
cout << equal_to<int>{}(1, 3) << endl;
map<int, less<int>> map1;
map<int, greater<int>> map3;
//逻辑类
cout << logical_and<int>{}(1,0) << endl;
一般配合容器一起做比较准则用的。
三.函数适配器
- 什么是函数适配器: 用来绑定函数调用时候的参数,让函数适应其他调用的用法
void print(int a, int b, int c)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
int num1 = 1, num2 = 2;
auto func = bind(print,std::placeholders::_1,std::placeholders::_2,666);
func(1, 2);
return 0;
}
此时函数可以传两个参数, 其实就是把函数最后一个参数给固定了,他还可以用来改变函数的传参位置:
void print(string a, int b, double c)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
auto func = bind(print, std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
//意思是,把第一个参数固定到第三个位置,第二个固定到第一个位置,第三个固定到第二个位置
func(2,1.1,"你好");
return 0;
}
传参位置改变,但输出顺序不变:
同样,函数适配器也是搭配容器来使用的:
int main()
{
vector<int> vecData = { 19,43,89,89,34,54,67,54 };
cout << count_if(vecData.begin(), vecData.end(), bind(greater<int>(), std::placeholders::_1, 60)) << endl;
cout << count_if(vecData.begin(), vecData.end(), [](int a) {return a > 60; }) << endl;
return 0;
}
输出结果是相同的,可以看到,有时候函数适配器跟Lambda表达式的功效是差不多的。
四.函数包装器
-
函数包装器是什么?就是把函数指针包装成一个对象。通过这个对象调用函数
- 一旦函数指针被函数包装器包装了,那这个包装器对象可以直接替换函数指针的用法去调用函数
-
函数包装器类的实力例化传参: function<函数返回值类型(参数类型)>
函数包装器可以包装普通函数,类中静态成员函数,以及类中的成员函数,但是在包装类中成员函数时必须有具体的对象,利用函数适配器包装,因为其实类成员函数在调用时有一个隐式的参数,就是一个具体的对象,所以要占个位。
void print(int a, int b, int c)
{
cout << a << " " << b << " " << c << endl;
}
class A
{
public:
void print(int a) //void print(int a, MM* mm);
{
cout << a << endl;
cout << "包装成员函数" << endl;
}
static void printStatic()
{
cout << "包装静态的函数" << endl;
}
protected:
};
int main()
{
//包装普通函数
function<void(int,int,int)> func1(print);
//包装类中静态成员函数
function<void()> func2(A::printStatic);
//包装类中成员函数
A a;
function<void(int)> func3(bind(&A::print,&a,std::placeholders::_1));
func1(1, 1, 1);
func2();
func3(2);
return 0;
}
五.智能指针
- 智能指针其实本质是一个模板类,一般使用是用的这个类的对象,而不是指针
- 智能指针体现在内存释放问题。用智能指针管理new的对象, 将不在需要手动delete
- 包含在头文件 <memory> 中
1.shared_ptr
- get() 函数: 返回数据的指针的引用
- use_count(): 返回的是管理对象的智能指针对象数
- swap():交换管理对象
- reset():重置管理对象
class MM
{
public:
void print()
{
cout << "打印" << endl;
}
~MM()
{
cout << "析构函数被调用" << endl;
}
protected:
};
void printData(shared_ptr<MM>& pMM)
{
pMM->print();
cout << "管理对象的指针数:" << pMM.use_count() << endl;
}
void freeFile(FILE* fp)
{
free(fp);
cout << "文件释放成功" << endl;
}
int main()
{
//1.基本用法
//new int(1111)申请内存做初始化
//1.1 做初始化
shared_ptr<int> pInt(new int(1111));
//1.2 访问数据
cout << *pInt << endl; //*指针
//shared_ptr<int> pNum = new int(222); 错误
//2.自定义类型
{
shared_ptr<MM> pMM(new MM);
//MM* p = new MM; //手动释放
pMM->print();
MM* pM = pMM.get();
}
//3.成员
//get()函数
int* pData = pInt.get();
//use_count()函数
shared_ptr<int> aptr(new int(10));
cout <<"get读取数据:" << *aptr.get() << endl;
cout << "正常使用:" << *aptr<<endl;
cout <<"管理对象的指针数:" << aptr.use_count() << endl;
shared_ptr<int> bptr(aptr);
cout << "管理对象的指针数:" << aptr.use_count() << endl;//2
cout << "管理对象的指针数:" << bptr.use_count() << endl;//2
shared_ptr<int> cptr;
if (!cptr)
{
cout << "空智能指针对象" << endl;
}
cptr = bptr;
cout << "管理对象的指针数:" << cptr.use_count() << endl;
//swap();
//reset(); 重新设置管理对象
shared_ptr<int> aa(new int(12));
shared_ptr<int> bb(new int(21));
aa.swap(bb);
cout << "aa:" << *aa << "\tbb:" << *bb << endl;
cout << "管理对象的指针数:" << aa.use_count() << endl;
bb.reset(new int(123));
cout << "bb:" << *bb << endl;
{
cout << "当做函数参数....." << endl;
shared_ptr<MM> pMM(new MM);
cout << "管理对象的指针数:" << pMM.use_count() << endl;
printData(pMM);
}
//当指向的是一块连续的内存时
//4.带删除器的写法
//删除器: 理解为自己写释放内存的过程
{
cout << "带删除器写法....." << endl;
shared_ptr<MM> p(new MM[4], [](MM* array) {delete[] array; });
}
shared_ptr<FILE> pf(fopen("1.txt", "w"), freeFile);
shared_ptr<int> pIntNum(new int(23));
return 0;
}
2.weak_ptr
- 弱引用指针,不会累计计数
- weak_ptr只能通过shared_ptr或者weak_ptr来构造
- 主要应用场景: 为了解决shared_ptr 循环引用内存导致无法释放问题
- 不可使用* 取值,能使用->取值
- 通过成员函数lock获取shared_ptr对象 然后再访问数据
主要解决的是循环引用的问题:
class MM
{
public:
MM() {}
void print()
{
cout << "打印" << endl;
}
~MM() { cout << "析构" << endl; }
protected:
};
class B;
class A
{
public:
A() { cout << "A" ;}
~A() { cout << "~A"; }
weak_ptr<B> b;
};
class B
{
public:
B() { cout << "B"; }
~B() { cout << "~B"; }
weak_ptr<A> a;
};
void testLoopUse()
{
cout << "循环引用....." << endl;
shared_ptr<A> aObject(new A);
shared_ptr<B> bObject(new B);
aObject->b = bObject;
bObject->a = aObject;
cout << endl << "计数:" << bObject.use_count() << endl;
cout << endl;
}
int main()
{
shared_ptr<MM> pMM(new MM);
shared_ptr<MM> pMM2 = pMM;
cout << "计数:" << pMM2.use_count() << endl;
weak_ptr<MM> pwMM(pMM);
cout << "计数:" << pMM2.use_count() << endl;
//(*pwMM).print();//weak_ptr不存在这种访问方式
//pwMM.lock(): shared_ptr对象
pwMM.lock().get()->print();
(pwMM.lock())->print();
{
cout << "循环引用........." << endl;
}
testLoopUse();
cout << endl;
shared_ptr<int> pMake = make_shared<int>(12);
cout << *pMake << endl;
//make_shared管理自定义类型,参数个数由构造函数决定
int* pp = pMake.get();
//delete pp; 手动释放会引发析构问题
return 0;
}
3.unique_ptr
- 禁止拷贝和赋值,独占型
- 任何时候unqiue_ptr操作管理对象,永远都只有一个有效
- 可以通过move函数转交所有权
- reset函数结合release函数移交所有权
注意写删除器的时候要多加一个参数:
using namespace std;
class MM
{
public:
MM() {}
MM(int num) :num(num) {}
int getNum() { return num; }
~MM() { cout << "MM:" << num << endl; };
protected:
int num=0;
};
struct DeleteMM
{
void operator()(MM* mm)
{
delete[] mm;
cout << "释放成功...." << endl;
}
};
int main()
{
unique_ptr<MM> pMM(new MM(1111));
//unique_ptr<MM> pMM2(pMM); // 错误
unique_ptr<MM> pMM3;
//pMM3 = pMM2; // 错误
cout << "MM num:" << pMM->getNum() << endl;
cout << "MM num:" << pMM.get()->getNum() << endl;
//移动拷贝
//move转交所有权
pMM3 = move(pMM);
cout << "MM num:" << pMM3->getNum() << endl;
//cout << "MM num:" << pMM->getNum() << endl; // pMM不在管理对象
unique_ptr<MM> pMM4(move(pMM3)); //MM&&
//可以重新设置
pMM.reset(new MM(1));
cout << "MM num:" << pMM->getNum() << endl;
unique_ptr<MM> pMM5;
pMM5.reset(pMM4.release());
cout << pMM5->getNum() << endl;
//删除器
{
unique_ptr<MM, DeleteMM> p(new MM[3]);
unique_ptr<MM, void(*)(MM*)> pp(new MM[3],
[](MM* mm) {delete[]mm; });
}
return 0;
}
六.正则表达式
正则是一种规则,它用来匹配(进而捕获、替换)字符串。这种规则需要“模式”、“字符串”这两样东西,“模式”根据正则规则,来处理“字符串”。这种规则被许多语言支持,C++11以后才支持正则。查文档吧,记住太累了!
具有特殊意义的元字符
\:\字符能够改变字符原本的含义
^:^字符指示字符串的头,且要求字符串以字符开头,不占位。\^表示一个真正的^符号。
$:$字符指示字符串的尾,且要求字符串以字符结尾,不占位。\$表示一个真正的$符号。
():分组,大正则中包含小正则。可以改变默认的优先级。在模式中可以使用\1来表示第一组已然捕获到的东西。
\b:指示字符串的边界(头/尾/空格左/空格右),字符\b要求边界的左边是字符,\b字符要求边界的右边是字符。
.:表示一个除了\n以外的任意一个字符。\.表示一个真正的.符号。
|:a|b a或b之一
[abc]:abc之中的任意一个
[^abc]: abc之外的
[a-z]: 任意小写字母
[^a-z]: 除了小写字母之外的
\w:任意一个字母数字下划线,等价于[(0-9)(a-z)(A-Z)(_)]
\W:字母数字下划线之外的,等价于[^(0-9)(a-z)(A-Z)(_)]
\d: 任意一个数字
\D: 除了数字之外的
\s: 空白符(空格、制表符、换页符)
量词元字符
*:字符*要求字符出现0到多次
+:字符+要求字符出现1到多次
?:字符?要求字符出现0次或1次
{n}:字符{n}要求字符出现n次
{n,}:字符{n,}要求字符出现n到多次
{n,m}:字符{n,m}要求字符出现n到m次
所以含有
\的元字符,在C++定义时,都要写成\\
校验数字的表达式
数字:^ [0 - 9] * $
n位的数字:^ \d{ n }$
至少n位的数字:^ \d{ n, }$
m - n位的数字: ^ \d{ m,n }$
零和非零开头的数字: ^ (0 | [1 - 9][0 - 9] )$
非零开头的最多带两位小数的数字: ^ ([1 - 9][0 - 9] *) + (.[0 - 9]{ 1,2 }) ? $
带1 - 2位小数的正数或负数: ^ (\ - ) ? \d + (.\d{ 1,2 }) ? $
正数、负数、和小数: ^ (\ - | \ + ) ? \d + (.\d + ) ? $
有两位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 2 }) ? $
有1~3位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 1,3 }) ? $
非零的正整数: ^ [1 - 9]\d * $ 或 ^ ([1 - 9][0 - 9] *) { 1, 3 }$ 或^ \ + ? [1 - 9][0 - 9] * $
非零的负整数: ^ \ - [1 - 9][]0 - 9"$ 或 ^-[1-9]\d*$
非负整数: ^ \d + $ 或 ^ [1 - 9]\d * | 0$
非正整数: ^ -[1 - 9]\d * | 0$ 或 ^ ((-\d + ) | (0 + ))$
非负浮点数: ^ \d + (.\d + ) ? $ 或 ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0$
非正浮点数: ^ ((-\d + (.\d + ) ? ) | (0 + (.0 + ) ? ))$ 或 ^ (-([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)) | 0 ? .0 + | 0$
正浮点数: ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * $ 或 ^ (([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9] + ) | ([0 - 9] * [1 - 9][0 - 9] *))$
负浮点数: ^ -([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)$ 或 ^ (-(([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9]) | ([0 - 9] * [1 - 9][0 - 9] *)))$
浮点数: ^ (-? \d + )(.\d + ) ? $ 或 ^ -? ([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0)$
校验字符的表达式
汉字: ^ [\u4e00 - \u9fa5]{ 0, }$
英文和数字: ^ [A - Za - z0 - 9] + $ 或 ^ [A - Za - z0 - 9]{ 4,40 }$
长度为3 - 20的所有字符: ^ .{3, 20}$
由26个英文字母组成的字符串: ^ [A - Za - z] + $
由26个大写英文字母组成的字符串: ^ [A - Z] + $
由26个小写英文字母组成的字符串: ^ [a - z] + $
由数字和26个英文字母组成的字符串: ^ [A - Za - z0 - 9] + $
由数字、26个英文字母或者下划线组成的字符串: ^ \w + $ 或 ^ \w{ 3,20 }$
中文、英文、数字包括下划线: ^ [\u4E00 - \u9FA5A - Za - z0 - 9_] + $
中文、英文、数字但不包括下划线等符号: ^ [\u4E00 - \u9FA5A - Za - z0 - 9] + $ 或 ^ [\u4E00 - \u9FA5A - Za - z0 - 9]{ 2,20 }$
可以输入含有 ^ %&',;=?$"等字符:[^%&', ; = ? $\x22] + 12 禁止输入含有~的字符:[^ ~\x22] +
特殊需求表达式
Email地址: ^ \w + ([-+.]\w + ) * @\w + ([-.]\w + ) * .\w + ([-.]\w + ) * $
域名:[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }(/ .[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }) + / . ?
InternetURL:[a - zA - z] + ://[^\s]* 或 ^http://([\w-]+.)+[\w-]+(/[\w-./?%&=])?$
手机号码: ^ (13[0 - 9] | 14[5 | 7] | 15[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9] | 18[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9])\d{ 8 }$
电话号码(0511 - 4405222、021 - 87888822):\d{ 3 } - \d{ 8 } | \d{ 4 } - \d{ 7 }
身份证号(15位、18位数字): ^ \d{ 15 } | \d{ 18 }$
短身份证号码(数字、字母x结尾): ^ ([0 - 9]) { 7, 18 }(x | X) ? $ 或 ^ \d{ 8,18 } | [0 - 9x]{ 8,18 } | [0 - 9X]{ 8,18 } ? $
帐号:(字母开头,允许5 - 16字节,允许字母数字下划线): ^ [a - zA - Z][a - zA - Z0 - 9_]{ 4,15 }$
密码:(以字母开头,长度在6~18之间,只能包含字母、数字和下划线): ^ [a - zA - Z]\w{ 5,17 }$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8 - 10之间): ^ (? = .\d)(? = .[a - z])(? = .[A - Z]).{8, 10}$
日期格式: ^ \d{ 4 } - \d{ 1,2 } - \d{ 1,2 } 一年的12个月(01~09和1~12): ^ (0 ? [1 - 9] | 1[0 - 2])$
一个月的31天(01~09和1~31): ^ ((0 ? [1 - 9]) | ((1 | 2)[0 - 9]) | 30 | 31)$
xml文件: ^ ([a - zA - Z] + -? ) + [a - zA - Z0 - 9] + \.[x | X][m | M][l | L]$
中文字符的正则表达式:[\u4e00 - \u9fa5]
双字节字符:[^ \x00 - \xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s * \r(可以用来删除空白行)
HTML标记的正则表达式:<(\S* ? )[^ > ] *>. ? < / \1> | <.* ? / > (复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式: ^ \s * | \s * $或(^ \s*) | (\s * $) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等))
腾讯QQ号:[1 - 9][0 - 9]{ 4, } (腾讯QQ号从10000开始)
中国邮政编码:[1 - 9]\d{ 5 }(? !\d) (中国邮政编码为6位数字)
IP地址:\d + .\d + .\d + .\d + (提取IP地址时有用) IP地址:((? : (? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d)\.) { 3 }(? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d))
如何使用?
- 匹配函数:bool regex_match(string str,regex reg)
- 替换函数:string regex_replace(string str,regex reg,string newStr)
- 截取处理字符串函数:bool regex_search(string str,smach result,regex reg)
//1.匹配 bool regex_match(string str,regex reg);
//匹配返回: true 不满足返回false
void test_regex_match()
{
#if 0
regex reg("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
string userName;
while (1)
{
cout << "请输入一个邮箱:";
cin >> userName;
bool result = regex_match(userName, reg);
if (result)
{
cout << "正确邮箱" << endl;
break;
}
}
#endif
//正则匹配,一定是完全匹配
string str = "ILoveyou1314";
regex reg("[a-z0-9]+"); //小写祖母或者数字多个
if (regex_match(str, reg))
{
cout << "匹配" << endl;
}
else
{
cout << "不匹配" << endl; //IL 不满足正则规则
}
//大小写问题 ,构造的时候可以忽略大小写
regex reg2("[a-z0-9]+", regex_constants::icase);
if (regex_match(str, reg2))
{
cout << "匹配" << endl;
}
else
{
cout << "不匹配" << endl; //IL 不满足正则规则
}
//字符串也可以直接充当正则规则
regex reg3("ILoveyou");
if (regex_match(str, reg3))
{
cout << "匹配" << endl;
}
else
{
cout << "不匹配" << endl; //1314
}
}
//2.正则替换string regex_replace(string str,regex reg, string newstr);
void test_regex_replace()
{
string str = "ILoveyou1314520IMissyou520me";
regex reg("\\d+");
cout << "str:" << regex_replace(str, reg, "我爱你") << endl;
cout << "oldstr:" << str << endl;
//如何控制替换
//1.只替换第一次出现的
//regex_constants::format_first_only
cout << "only first:" << regex_replace(str, reg, "我爱你", regex_constants::format_first_only)
<< endl;
//2.不拷贝
cout << "no copy:" << regex_replace(str, reg, "我爱你", regex_constants::format_no_copy)
<< endl;
//3.默认方式
cout<<"default:"<< regex_replace(str, reg, "我爱你", regex_constants::format_default)
<< endl;
//4.sed
cout << "sed:" << regex_replace(str, reg, "我爱你", regex_constants::format_sed)
<< endl;
}
//3.截取处理字符串
//bool regex_search(string,str,smatch result,regex reg)
void test_regex_search()
{
//正常截取处理 匹配项
string str = "ILoveyou1314IMissyou520me";
smatch result;
bool flag = regex_search(str, result, regex("\\d+"));
if (flag)
{
cout << "size:" << result.size() << endl;
for (int i = 0; i < result.size(); i++)
{
cout << result.str() << endl; //字符串
}
cout << "pre:" << result.prefix() << endl;//前缀
cout << "suf:" << result.suffix() << endl;//后缀
}
cout << "str:" << str << endl;
//匹配后面: result.suffix作为循环条件去持续截即可
// srgex_iterator
regex rule("\\d+");
sregex_iterator pos(str.begin(), str.end(), rule);
sregex_iterator end; //end_of_iterator
while (pos != end)
{
cout << pos->str() << endl;
pos++;
}
//拆解字符串
//sregex_token_iterator(iterator begin,iteartor end,regex regex, int flag);
//flag: 0: 所有匹配的,-1 存储所有不匹配的
regex regexRule("\\d+");
sregex_token_iterator Begin(str.begin(), str.end(), regexRule, 0);
sregex_token_iterator End;
while (Begin != End)
{
cout << Begin->str() << endl;
Begin++;
}
cout << endl;
}
978

被折叠的 条评论
为什么被折叠?



