智能指针对象是一个对象,可以充当函数参数也可以充当函数返回值。
智能体现在内存管理(内存的申请和释放)(在对象的析构函数中做了资源的释放)
用智能指针管理new的对象将不在需要手动delete
智能指针本质上是一个模板类,一般使用的是这个类的对象,而不是指针(弱化指针)
shared_ptr
-
get() 函数: 返回数据的指针的引用
-
use_count(): 返回的是管理对象的智能指针对象数
-
swap():交换管理对象
-
reset():重置管理对象
#include<iostream> #include<memory> #include<string> #include<cstdio> using namespace std; class MM { public: void print() { cout << "打印函数" << endl; } ~MM() { cout << "析构函数被调用" << endl; } protected: }; void printData(shared_ptr<MM>& pMM) {//这里用引用就不会产生拷贝本,下面的use_count数就不会变 pMM->print(); cout << "管理该对象的指针个数:" << pMM.use_count() << endl; } void freeFile(FILE* file) { free(file); cout << "文件释放成功" << endl; } int main() { //1.基本用法 //1.1初始化 shared_ptr<int> pInt(new int(1111)); //不能采用赋值的方式初始化 //shared_ptr<int> pNum = new int(111); 错误 //1.2使用(访问数据) cout << *pInt << endl; //2.自定义类型测试自动delete { //测试对象死亡的时候是否调用了析构函数,用一个作用域分辨符来括起来 shared_ptr<MM> pMM(new MM); //MM* p = new MM; 这个就需要手动释放内存 pMM->print(); MM* pM = pMM.get(); //返回new MM的地址 } //3.常用成员函数 //get()函数 //这里管理int型的数据,get函数就可以返回int*类型的指针 int* pData = pInt.get(); //use_count()函数 shared_ptr<int> a(new int(10)); cout << "正常访问:" << *a << endl; cout << "get读取数据:" << *a.get() << endl; cout << "管理该对象的指针个数:" << a.use_count() << endl; //通过上面的指针做拷贝构造 shared_ptr<int> b(a);//意味着b这个指针也是管理10这个整数的 cout << "管理该对象的指针个数:" << a.use_count() << endl; cout << "管理该对象的指针个数:" << b.use_count() << endl; shared_ptr<int> c; if (!c) { cout << "空智能指针对象" << endl; } c = b; cout << "管理该对象的指针个数:" << c.use_count() << endl; shared_ptr<int> aa(new int(12)); shared_ptr<int> bb(new int(21)); cout << "aa" << *aa << "\tbb" << *bb << endl; aa.swap(bb); cout << "aa" << *aa << "\tbb" << *bb << endl; bb.reset(new int(123)); cout << "new bb\t" << *bb << endl; { cout << "当作函数参数。。。。" << endl; shared_ptr<MM> pMM(new MM); cout << "管理该对象的指针个数:" << pMM.use_count() << endl; printData(pMM); } //4.带删除器的写法 //删除器:理解为自己写释放内存的过程 //有些内存不是正常new出来的内存,但也算是动态内存,这时候就要手动写释放过程,如用智能指针管理对象数组,包括c语言里面的文件操作 { cout << "删除器写法————" << endl; //shared_ptr<MM> p(new MM[4]); 报错,正常的不能释放一段内存 shared_ptr<MM> p(new MM[4], [](MM* array) {delete[] array; }); } //c语言文件操作 shared_ptr<FILE> pf(fopen("1.txt", "w"),freeFile); //通过make_shared<int>() shared_ptr<int> pMake = make_shared<int>(12); cout << *pMake << endl; //make_shared如果用来管理自定义类型,那么参数个数由构造函数决定 //当我们用get方法去获取指针的时候,一定不能手动释放,否则会重复析构 int* ppp = pMake.get(); //delete[] ppp; return 0; }
weak_ptr
-
弱引用指针,不会累计计数
-
weak_ptr只能通过原有的shared_ptr或者weak_ptr来构造,不能直接管理数据
-
应用场景较为单一
-
主要应用场景: 为了解决shared_ptr 循环引用内存导致无法释放问题
-
不可使用* 取值,只能使用->取值
-
通过成员函数lock获取shared_ptr对象 然后再访问数据
#include<iostream> #include<memory> using namespace std; 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; protected: }; class B { public: B() { cout << "B"; } ~B() { cout << "~B"; } weak_ptr<A> a; protected: }; 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 << "use_count:" << "\t" << pMM.use_count() << endl; weak_ptr<MM> pwMM(pMM); cout << "use_count:" << "\t" << pMM.use_count() << endl; //(*pwMM).print(); 报错 pwMM.lock().get()->print(); //pwMM.lock()获取的是share_ptr对象 pwMM.lock()->print(); { cout << "循环引用————" << endl; } testLoopUse(); return 0; }
unique_ptr
-
禁止拷贝和赋值,独占型
-
任何时候unqiue_ptr操作管理对象,永远都只有一个有效
-
可以通过move函数转交所有权
-
reset函数结合release函数移交所有权
#include<iostream> #include<string> #include<memory> 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 = pMM; //访问数据 cout << "MM num:" << pMM.get()->getNum() << endl; cout << "MM num:" << pMM->getNum() << endl; //如果真的需要拷贝赋值,智能通过移动拷贝或者move转交所有权 //移动拷贝 //move转交所有权 pMM3 = move(pMM); //pMM不在存在 cout << "MM num:" << pMM3->getNum() << endl; unique_ptr<MM> pMM4(move(pMM3)); //MM&&移动构造 //移动后可以重新构造 pMM.reset(new MM(123)); cout << "MM num" << pMM->getNum() << endl; unique_ptr<MM> pMM5; pMM5.reset(pMM4.release()); cout << pMM5->getNum() << endl; //删除器 { unique_ptr<MM, deleteMM> mmArray(new MM[4]); unique_ptr<MM, void(*)(MM*)> pp(new MM[3], [](MM* mm) {delete[] mm; }); } return 0; }
C++正则表达式
正则是一种规则,它用来匹配(进而捕获、替换)字符串。这种规则需要“模式”、“字符串”这两样东西,“模式”根据正则规则,来处理“字符串”。这种规则被许多语言支持,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:字母数字下划线之外的,等价于[]
\d: 任意一个数子
\D: 除了数字之外的
\s: 空白符(空格、制表符、换页符)
量词元字符
*:字符*要求字符出现0到多次 {0,}
+:字符+要求字符出现1到多次 (\w) {1,}
?:字符?要求字符出现0次或1次 {0,1}
{n}:字符{n}要求字符出现n次
{n,}:字符{n,}要求字符出现n到多次 {0,}
{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))
#include<regex> #include<iostream> #include<cstdbool> #include<string> using namespace std; void test_regex_match() { #if 0 //1.基本单元的组成 //2.复制过程中\改成 //匹配 返回值布尔类型,返回两个参数,一个是要匹配的字符串,一个是正则规则 //regex_match(string str, regex reg) //匹配返回:true false regex reg("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"); string userName; while (1) { cout << "请输入一个邮箱:"; cin >> userName; bool result = regex_match(userName, reg); if (result == true) { cout << "正确邮箱" << endl; break; } } #endif //正则匹配一定是完全匹配,一点不一样的都不行 string str = "Iloveyou1314"; regex reg("[a-z0-9]+"); if (regex_match(str, reg)) { cout << "匹配" << endl; } else { cout << "不匹配" << endl; //I不满足正则规则是大写的 } //大小写问题,c++提供带参构造(构造的时候可以忽略大小写) regex reg2("[a-z0-9]+", regex_constants::icase); if (regex_match(str, reg2)) { cout << "匹配" << endl; } else { cout << "不匹配" << endl; } //字符串也可以直接充当正则规则 regex reg3("Iloveyou"); //必须要一模一样才匹配 if (regex_match(str, reg3)) { cout << "匹配" << endl; } else { cout << "不匹配" << endl; //1314不满足 } } //正则替换 regex_replace(string str,regex reg,string newstr); void test_regex_replace() { string str = "ILoveyou1314IMissyou520me"; regex reg("\\d+"); //多个数字 cout << "str;" << regex_replace(str, reg, "我爱你") << endl; //不改变原字符串 //如何控制替换 //只替换第一次出现的 cout << "only first:" << regex_replace(str, reg, "我爱你", regex_constants::format_first_only) << endl; //不拷贝 cout << "no copy:" << regex_replace(str, reg, "我爱你", regex_constants::format_no_copy) << endl; //默认方式 cout << "default:" << regex_replace(str, reg, "我爱你", regex_constants::format_default) << endl; //(不传参) } //截取(捕获)处理字符串 //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 (auto v : result) { cout << v.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,iterator.end,regex regex,int flag) //flag 0:所有匹配到的 flag -1:存储所有不匹配的 regex regexRule("\\d+"); sregex_token_iterator Begin(str.begin(), str.end(), rule, -1); sregex_token_iterator End; while (Begin != End) { cout << Begin->str() << endl; Begin++; } } int main() { test_regex_match(); test_regex_replace(); test_regex_search(); return 0; }
-