文章目录
- [1. 正则表达式](#1. 正则表达式)
-
- [1.1 regex_search](#1.1 regex_search)
- [1.2 regex_match](#1.2 regex_match)
- [1.3 regex_replace](#1.3 regex_replace)
- [1.4 正则表达式补充](#1.4 正则表达式补充)
- [2. 多态](#2. 多态)
- [3. 重载](#3. 重载)
- [4. 模板](#4. 模板)
-
- [4.1 函数模板](#4.1 函数模板)
- [4.2 类模板](#4.2 类模板)
- [4.3 模板显式实例化](#4.3 模板显式实例化)
- [4.4 模板特化](#4.4 模板特化)
-
- [4.4.1 全模板](#4.4.1 全模板)
- [4.4.2 偏模板](#4.4.2 偏模板)
- [5. 异常处理](#5. 异常处理)
-
- [5.1 捕获异常例子:vector的边界检查](#5.1 捕获异常例子:vector的边界检查)
- [5.2 抛出异常+捕获异常例子:除法抛出除零异常,调用方捕获它](#5.2 抛出异常+捕获异常例子:除法抛出除零异常,调用方捕获它)
- [6. 零散话题](#6. 零散话题)
-
- [6.1 class和struc的异同](#6.1 class和struc的异同)
-
- [6.1.1 相同点](#6.1.1 相同点)
- [6.1.2 不同点](#6.1.2 不同点)
- [6.2 什么是移动构造函数吗,拷贝构造函数](#6.2 什么是移动构造函数吗,拷贝构造函数)
1. 正则表达式
1.1 regex_search
找字符串中匹配模式的子串。从字符串开始位置搜索,直到找到第一个匹配项(如果需要拿到所有匹配项,需要使用regex_iterator)就停止继续搜索并返回true,未能找到匹配项则返回false。
c++
/*
regex_search(text, pat, matchResult)
text: string, 待匹配字符串
pat: regex, 正则表达式
matchResult(可选): smatch, 存放匹配结果。结果子串matchResult.str(),结果子串在原字符串中的位置matchResult.position()。
返回: bool
*/
#include <iostream>
#include <regex>
#include <string>
void NonIterativeMtach(std::string text, std::regex pattern){
std::smatch match; // 用smatch保存匹配结果
// 查找第一个匹配项: John Doe, 25 years old
if (std::regex_search(text, match, pattern)) {
std::cout << "First match found:" << std::endl;
std::cout << "Whole match: " << match.str(0) << std::endl;
// 遍历各个捕获组
for (size_t i = 1; i < match.size(); ++i) {
std::cout << "Capture group " << i << ": " << match.str(i) << std::endl;
}
// 继续查找后续匹配项: Jane Smith, 30 years old
std::string remainingText = match.suffix().str();
while (std::regex_search(remainingText, match, pattern)) {
std::cout << "\nNext match found:" << std::endl;
std::cout << "Whole match: " << match.str(0) << std::endl;
for (size_t i = 1; i < match.size(); ++i) {
std::cout << "Capture group " << i << ": " << match.str(i) << std::endl;
}
remainingText = match.suffix().str();
}
} else {
std::cout << "No match found." << std::endl;
}
}
// 使用迭代器遍历匹配结果
void IterativeMatch(std::string text, std::regex pattern){
std::smatch match;
std::sregex_iterator it(text.begin(), text.end(), pattern); // 指向第一个匹配项的迭代器
std::sregex_iterator end; // 结束迭代器
if(it == end){
std::cout << "No match found." << std::endl;
return;
}
for(; it!= end; it++){
match = *it;
std::cout << "Whole match: " << match.str(0) << std::endl;
for (size_t i = 1; i < match.size(); ++i) {
std::cout << "Capture group " << i << ": " << match.str(i) << std::endl;
}
std::cout << std::endl;
}
}
int main() {
std::string text = "John Doe, 25 years old. Jane Smith, 30 years old.";
std::regex pattern("(\\w+) (\\w+), (\\d+) years old");
// 仅判断否匹配:std::regex_search(text, pattern)
if(std::regex_search(text, pattern)){
std::cout << "matched!" << std::endl;
}
NonIterativeMtach(text, pattern);
std::cout << std::endl << std::endl;
IterativeMatch(text, pattern);
return 0;
}
1.2 regex_match
检查整个输入字符串是否完全匹配模式,如果匹配则返回true, 否则返 嘻嘻嘻尺寸小星星 false。
c++
/*
regex_match(text, pat, matchResult):
text: string, 待匹配字符串
pat: regex, 正则表达式
matchResult(可选): smatch, 存放匹配结果。结果子串matchResult.str(),结果子串在原字符串中的位置matchResult.position()。
返回: bool
*/
#include <iostream>
#include <regex>
int main(){
std::string text1 = "John Doe, 25 years old";
std::regex pattern("(\\w+) (\\w+), (\\d+) years old");
std::smatch matches;
if(std::regex_match(text1, matches, pattern)){
std::cout << "text1 matched!" << std::endl;
for(size_t i = 0; i < matches.size(); i++){
std::cout << "matches[" << i << "]: " << matches[i] << std::endl;
}
}else{
std::cout << "text1 not matched!" << std::endl;
}
// 整个输入字符串匹配才返回true
std::string text2 = "John Doe, 25 years old. Jane Smith, 30 years old.";
if(std::regex_match(text1, pattern)){
std::cout << "text2 matched!" << std::endl;
}else{
std::cout << "text2 not matched!" << std::endl;
}
}
1.3 regex_replace
所有匹配项都被替换为subs后返回结果字符串。
c++
/*
regex_replace(text, pat, subs)
text: string, 待匹配字符串
pat: regex, 正则表达式
subs: 替换字符串
flags(可选):控制匹配行为的匹配标志(大小写等)
返回: 匹配替换后的字符串
*/
#include <iostream>
#include <regex>
#include <string>
int main() {
std::string text = "John Doe, 25 years old. Jane Smith, 30 years old.";
// 简单替换
std::regex pattern1(",");
std::string replacement1 = " is";
std::string result1 = std::regex_replace(text, pattern1, replacement1);
// 使用捕获组替换
std::regex pattern2("(\\w+) (\\w+), (\\d+) years old");
std::string replacement2 = "$2 $1 is $3 years old";
std::string result2 = std::regex_replace(text, pattern2, replacement2);
std::cout << "Original text: " << text << std::endl;
std::cout << "Replaced text1: " << result1 << std::endl;
std::cout << "Replaced text2: " << result2 << std::endl;
return 0;
}
1.4 正则表达式补充
-
常用正则表达式元字符:
.
:任意一个除换行符外的字符。*
:量词(贪婪),匹配0次或者多次。+
:量词(贪婪),匹配1次或多次。?
:作量词时,匹配0次或1次;作非贪婪开关时,使贪婪量词转为非贪婪量词,如.*?
,.+?
。
-
正则表达式的转义序列(C++):
\\d
匹配数字,\\w
匹配单词,\\b
单词边界 -
常用的正则表达式
- 密码校验:
^(?=.*[a-zA-Z])(?=.*\\d).+$
- 密码校验:
-
零宽断言:正则表达式中,只判断不纳入匹配结果。
- 正向先行断言:
"apple(?=\\d+)"
中括号内的表达式子是正向先行断言,表示匹配apple时,如果apple前是数字,则匹配成功,否则匹配失败。匹配结果是apple而不是apple加数字。 - 正向后行断言:
"(?<=¥)\\d+"
括号内的表达式是正向后行断言,表示匹配数字时,如果数字前是符号¥
,则匹配成功,否则匹配失败,匹配结果是数字而不是¥
加数字。 - 负向先行断言:
"apple(?!\\d+)"
如果apple后不是数字,匹配成功,否则匹配失败。 - 负向后行断言:
"(?<!¥)\\d+"
如果数字前不是¥
,匹配成功,否则匹配失败。
- 正向先行断言:
-
性能考虑:编译正则表达式;减少使用非贪婪量词。
2. 多态
C++多态机制:静态多态(编译时多态)和动态多态(运行时多态)。
静态多态:函数重载;模板。ref:本文档第3章和第4章。
动态多态:虚函数和继承(运行时才确定对象的行为:派生类重写基类的虚函数f(),基类指针在运行时指向派生类对象,则将调用派生类对象的重写的f())。ref:https://blog.csdn.net/backlash321/article/details/146068181
3. 重载
函数签名:函数名,参数列表
函数头:返回类型,函数名,参数列表
函数声明:函数头
函数定义:函数头+函数体
重载的函数要求:函数名相同,函数参数列表不同,即函数签名相同。如果两个函数仅仅函数返回类型不同,那么这两个函数不构成重载(会报错)。
3.1 普通函数重载
c++
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
3.2 运算符重载
重载运算符要求:至少有一个参数是自定义类型;只能对已有运算符重载(+
、-
、*
、/
) ,不能重载部分运算符( .
、::
、?:
、sizeof
)
3.2.1 重载二元运算符
重载 "<"
c++
// 作为成员函数来重载二元运算符, String对象y、z,y < z解析为y.operator< (z)
class String{
public:
bool operator<( const String & ) const;
...
};
// 作为非成员函数的二元重载运算符,y < z将被解析为operator(y, z)
bool operator<(const String &, const String & );
重载"<<"和">>"
c++
#ifndef PHONENUMBER_H
#define PHONENUMBER_H
#include <iostream>
#include <string>
// define
class PhoneNumber
{
friend std::ostream &operator<<( std::ostream &, const PhoneNumber & );
friend std::istream &operator>>( std::istream &, PhoneNumber & );
private:
std::string areaCode;
std::string exchange;
std::string line;
};
#endif
3.2.2 重载一元运算法
重载"!"
c++
// 成员函数形式
class String{
public:
bool operator!() const;
...
};
// 非成员函数形式
bool operator!( const String & );
重载"++"
前置++
c++
// 成员函数形式, 编译器解析为d1.operator++()
class Date{
public:
Date &operator++();
...
}
// 非成员函数形式,编译器解析为operator++( d1 )
Date &operator++( Date & );
后置++
c++
// 成员函数形式,编译器解析为d1.operator++(0),参数0仅仅是为了编译器区分前置/后置
class {
public:
Date opereator++( int ); // 需要占位参数
...
}
// 非成员函数形式,编译器解析为operator++( d1, 0 ),参数0仅仅是为了编译器区分前置/后置
Date operator++( Date &, int );
4. 模板
4.1 函数模板
c++
// 单个类型模板
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// 多个类型模板
template <typename T1, typename T2>
void display(const T1& a, const T2& b) {
cout << a << endl;
cout << b << endl;
}
4.2 类模板
c++
template <typename T>
class Stack {
private:
T* data;
int size;
int capacity;
public:
Stack(int cap) : capacity(cap), size(0) {
data = new T[capacity];
}
~Stack() {
delete[] data;
}
void push(T value) {
if (size < capacity) {
data[size++] = value;
}
}
T pop() {
if (size > 0) {
return data[--size];
}
return T();
}
};
4.3 模板显式实例化
在代码中明确指定模板参数的具体类型,让编译器在特定位置生成该类型的模板实例,而不是等到模板被实际使用时才进行实例化。这样可以提升编译效率,避免连接错误。
c++
#include <iostream>
// 函数模板
template <typename T>
T add(T a, T b) {
return a + b;
}
// 类模板
template <typename T>
class Container {
public:
Container(T value) : data(value) {}
T getData() const {
return data;
}
private:
T data;
};
// 函数模板显式实例化
template int add<int>(int, int);
template float add<float>(float, float);
// 类模板显式实例化
template class Container<double>;
int main() {
int result1 = add(1, 2);
float ret = add(1.2, 1.3);
std::cout << "Result of add(1, 2): " << result1 << std::endl;
std::cout << "Result of add(1.2, 1.3): " << ret << std::endl;
Container<double> c(3.14);
std::cout << "Data in Container: " << c.getData() << std::endl;
return 0;
}
4.4 模板特化
通用模板有一套实现逻辑,但是希望对某些具体的类型走其他逻辑而不是同一的通用模板逻辑。这时可以使用模板特化。可以理解为重写父模板。模板特化分为全特化(子模板将父模板的全部模板参数都指定成具体类型)和偏特化(子模板将父模板的部分模板参数指定为具体类型,其他模板参数扔指定为模板参数)
4.4.1 全模板
c++
#include <iostream>
// 函数模板
template <typename T1, typename T2>
T1 max(T2 a, T2 b) {
return (a > b) ? a : b;
}
// 函数模板全特化
template <>
bool max<bool, const char*>(const char* a, const char* b) {
return (std::strcmp(a, b) > 0) ? a : b;
}
int main() {
int result1 = max<int, int>(1, 2); // 调用时指定使用通用模板
std::cout << "Max of 1 and 2: " << result1 << std::endl;
bool result2 = max<bool, const char *>("ab", "bb"); // 调用时指定使用全特化模板
std::cout << "Max of 'ab' and 'bb': " << result2 << std::endl;
return 0;
}
4.4.2 偏模板
c++
#include <iostream>
// 类模板
template <typename T1, typename T2>
class Pair {
public:
Pair(T1 first, T2 second) : first(first), second(second) {}
void print() const {
std::cout << "Generic Pair: " << first << ", " << second << std::endl;
}
private:
T1 first;
T2 second;
};
// 类模板偏特化
template <typename T>
class Pair<T, int> {
public:
Pair(T first, int second) : first(first), second(second) {}
void print() const {
std::cout << "Partial Specialization Pair: " << first << ", " << second << std::endl;
}
private:
T first;
int second;
};
int main() {
Pair<double, char> p1(3.14, 'A'); // 使用父模板
p1.print();
Pair<double, int> p2(3.14, 10); // 使用特化的子模板
p2.print();
return 0;
}
5. 异常处理
-
异常处理需要完成两件工作:抛出异常(throw),捕获异常(try-catch)。
-
关于异常在调用栈中的传递:f1()调用f2(),f2()调用f3(),f3()抛出异常,选则在f1()还是f2()中捕获它?
-
在f2()中处理异常的理由:如果 f2() 能够根据异常信息进行局部的错误恢复或处理,则不需要将异常向上传递(如f2() 可以尝试重新调用 f3() 或者使用备用方案来完成任务),这样可以保持较好的封装性。
-
在f1()中处理异常的理由:如果f2()不足以处理异常,则应该向上传递给f1()。要求在一个较为宏观的层面全面而通用地处理异常和记录异常,则在f1()处理异常更合适(如记录日志、进行资源清理或者通知用户)。
-
5.1 捕获异常例子:vector的边界检查
c++
#include < stdexcept >
// vector对象的at函数提供边界检查和抛出异常的功能,at(ind)中ind超出vector现有元素范围,则抛出out_of_range异常(定义在<stdexcept>中)
try{
cout << v.at( 8 ) << endl;
}
catch (out_of_range &ex){
cout << ex.what() <<endl; // 显示存储在异常对象ex中存储的错误信息
}
5.2 抛出异常+捕获异常例子:除法抛出除零异常,调用方捕获它
c++
#include <iostream>
// 定义一个除法函数,可能会抛出除零异常
double divide(double a, double b) {
if (b == 0) {
// 抛出一个字符串类型的异常
throw "Division by zero!";
}
return a / b;
}
int main() {
double num1 = 10.0;
double num2 = 0.0;
try {
double result = divide(num1, num2);
std::cout << "Result: " << result << std::endl;
}
catch (const char* error) {
std::cerr << "Error: " << error << std::endl;
}
std::cout << "After exception" << std::endl;
return 0;
}
/*
Error: Division by zero!
After exception
*/
6. 零散话题
6.1 class和struc的异同
6.1.1 相同点
都可以用来定义数据类型,内部可以定义数据成员和函数成员。
c++
// struct的基本用法
struct MyStruct{ // 命名结构体
int a;
int b;
void display(){
cout << "display: ";
cout << "a = " << a << ", b = " << b << endl;
}
MyStruct(int a, int b): a(a), b(b){}
};
struct { // 匿名结构体
int a;
int b;
void display(){
cout << "display: ";
cout << "a = " << a << ", b = " << b << endl;
}
} anonymousVar; // 使用匿名结构体声明的变量
// class的基本用法
class SqList{
public:
SqList(int capacity=100){
this->len = 0;
this->capacity = 0;
this->dataPtr = new int[capacity];
if(this->dataPtr != NULL){
this->capacity = capacity;
// init
for(size_t i=0; i<capacity; i++){
dataPtr[i] = 0;
}
}
}
~SqList(){
len = 0;
capacity = 0;
delete[] dataPtr;
}
void traverse(){
for(size_t i=0; i<len; i++){
cout << dataPtr[i] << ' ';
}
cout << endl;
}
void display(){
cout << "display: ";
for(size_t i=0; i<capacity; i++){
cout << dataPtr[i] << ' ';
}
cout << endl;
}
private:
int len;
int capacity;
int * dataPtr;
};
int main() {
SqList mySqList(10);
MyStruct myStructInstance(1,2);
mySqList.display();
myStructInstance.display();
}
6.1.2 不同点
- 默认成员变量权限不同,struct默认成员变量为public成员变量,class默认成员变量为private成员变量。
c++
struct MyStruct{
int a;
int b;
MyStruct(int a, int b): a(a), b(b){}
};
class MyClass{
int a;
int b;
MyClass(int a, int b): a(a), b(b){}
};
int main(){
MyStruct myStructInstance(1, 2);
cout << "a = " << myStructInstance.a << ", b = " << myStructInstance.b << endl;
// MyClass myClassInstance(1, 2); // 出错,构造函数是private的
// cout << "a = " << myClassInstance.a << ", b = " << myClassInstance.b << endl; // 出错,成员变量是private的
return 0;
}
- 默认继承方式不同,struct默认public继承,class默认praivate继承
c++
struct BaseStruct{
int a;
BaseStruct(int a):a(a){}
};
struct DerivedStruct: BaseStruct{
int b;
DerivedStruct(int a, int b):BaseStruct(a), b(b){}
};
class BaseClass{
int a;
BaseClass(int a): a(a){}
};
class DerivedClass: BaseClass{
int b;
// DerivedClass(int a, int b): BaseClass(a), b(b){} // 基类构造函数为private,派生类无法访问
};
int main(){
DerivedStruct myDerivedStructInstance(1, 2);
cout << "a = " << myDerivedStructInstance.a << ", b = " << myDerivedStructInstance.b << endl;
// MyClass myClassInstance(1, 2); // 出错,构造函数是private的
// cout << "a = " << myClassInstance.a << ", b = " << myClassInstance.b << endl; // 出错,成员变量是private的
return 0;
}
6.2 什么是移动构造函数吗,拷贝构造函数
拷贝构造函数:创建对象的副本时使用。拷贝构造函数被调用的时机:用一个对象初始化另一个同类型对象时;函数传参时;函数返回时;以某类型对象为元素的容器在插入对象时,会调用该类型的拷贝构造函数。
移动构造函数:定义移构造函数,函数返回值时不再需要拷贝临时对象(右值)控制的资源,而只需要将临时对象(右值)的资源控制权交给新对象即可。返回值对象就是临时对象(右值)。
c++
class MyClass{
public:
// 自定义构造函数
MyClass(int cap){
this->len = 0;
this->cap = cap;
this->data = new(std::nothrow) int[cap]; // 分配内存失败不会返回bad_alloc,而是返回nullptr
if(this->data != nullptr){
for(size_t i=0; i < cap; i++){
this->data[i] = 0;
}
}
}
// 拷贝构造函数
MyClass(MyClass &other){
cout << "Copy Constructor is called!" << endl;
len = other.len;
cap = other.cap;
// 深拷贝
data = new(std::nothrow) int[other.cap];
if(data != nullptr){
for(size_t i=0; i < cap; i++){
data[i] = other.data[i];
}
}
}
// 移动构造函数
MyClass(MyClass &&other){
cout << "Move Constructor is called!" << endl;
len = other.len;
cap = other.cap;
// 浅拷贝
data = other.data;
other.data = nullptr; // 避免右值对象被销毁时释放data
}
// 析构函数
~MyClass(){
delete[] data;
}
void display(){
for(size_t i=0; i<cap; i++){
cout << data[i] << ' ';
}
cout << endl;
}
private:
int len;
int cap;
int *data;
};
MyClass f(MyClass a){ // 发生拷贝
cout << "just enter f()" << endl;
cout << "before return f()" << endl;
return a;
}
int main(){
MyClass myClassIns(10);
myClassIns.display();
MyClass b = f(myClassIns); // 传参调用拷贝构造函数;返回调用移动构造函数(返回值是临时对象,作为右值,将资源控制权交给左值b对象,导致调用移动构造函数)
return 0;
}
/*
0 0 0 0 0 0 0 0 0 0
Copy Constructor is called!
just enter f()
before return f()
Move Constructor is called!
*/