C++笔记
哈希表
unordered_set
unordered_map
教程原文
unordered_map 就是一个键值对,一个key对应一个value,map[key] = value
访问它的内容,也可以通过查询出来的 iterator->first , iterator->second这样的来访问
迭代器
pair的用法
博客原文
pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。
其标准库类型–pair类型定义在#include 头文件中,定义如下:
类模板:template<class T1,class T2> struct pair
参数:T1是第一个值的数据类型,T2是第二个值的数据类型。
功能:pair将一对值(T1和T2)组合成一个值,
这一对值可以具有不同的数据类型(T1和T2),
两个值可以分别用pair的两个公有函数first和second访问。
无参数的构造函数
对于没有参数的构造函数,在创建类的时候,要用:
ClassName obj{};
或者
ClassName obj;
printf 和sprintf

int a=12;
printf("八进制 %o\n",a);
printf("十六进制 %x\n",a);
printf("十进制 %d\n",a);
char* date = new char[9];
int sub = 999145;
sprintf(date,"%X", sub);//转为十六进制字符串,大写
sprintf(date,"%x", sub);//转为十六进制字符串,小写
sprintf(date,"%o", sub);//转为八进制字符串
sprintf(date,"%d", sub);//转为十进制字符串
整数转为二进制字符串
int n = 10;
string s;
for(int i =31; i>=0; i--){
s += to_string((n>>i) & 1);
}
cout<<s<<endl;
C++字符串转其他类型
其他转string
std::to_string(int)
std::to_string(long)
std::to_string(long long)
std::to_string(float)
std::to_string(double)
std::to_string(long double)
字符串转整形
stoi()
int stoi (const string& str, size_t* idx = 0, int base = 10);
int stoi (const wstring& str, size_t* idx = 0, int base = 10);
str:表示所要转化的字符串
idx:表示想要str中开始转化的位置,默认为从第一个字符开始。
例如stoi(“123abcd”,&p),返回的p指向a所在,也可以把p的位置理解为数值部分的结束位置
base:表示要用的进制(如2进制、16进制,默认为10进制)转化为int类型十进制数字,0是让他自动推断。
#include <string>
int n=stoi("89"); //--> 89
int i_dec = std::stoi ("2001abcd",&sz); //--> 2001,sz返回的是'a'所在的位置
int i_zero = std::stoi ("-000123",&sz); //--> -123
int i_hex = std::stoi ("40c3",nullptr,16); //--> 16579
int i_bin = std::stoi ("-10010110001",nullptr,2); //--> -1201
int i_auto = std::stoi ("0x7f",nullptr,0); //--> 127
stol()
string转long
long stol (const string& str, size_t* idx = 0, int base = 10);
long stol (const wstring& str, size_t* idx = 0, int base = 10);
用法类似于stoi
stoul()
string转unsingned long
unsigned long stoul (const string& str, size_t* idx = 0, int base = 10);
unsigned long stoul (const wstring& str, size_t* idx = 0, int base = 10);
用法类似于stoi
stoll()
string转long long
long long stoll (const string& str, size_t* idx = 0, int base = 10);
long long stoll (const wstring& str, size_t* idx = 0, int base = 10);
用法类似于stoi
stoull()
string转unsingned long long
unsigned long long stoull (const string& str, size_t* idx = 0, int base = 10);
unsigned long long stoull (const wstring& str, size_t* idx = 0, int base = 10);
用法类似于stoi
字符串转浮点型
stof()
float stof (const string& str, size_t* idx = 0);
float stof (const wstring& str, size_t* idx = 0);
这里就不能选择类型了
string str1 = "0x444";
cout<<stoi(str1, nullptr, 16)<<endl;//1092
cout<<stof(str1)<<endl;//1092
string str2 = "0101010";
cout<<stoi(str2, nullptr, 2)<<endl;//42
cout<<stof(str2)<<endl;//101010
string str3 = "8.889";
cout<<stoi(str3, nullptr, 10)<<endl;//8
cout<<stof(str3)<<endl;//8.889
stod()
double stod (const string& str, size_t* idx = 0);
double stod (const wstring& str, size_t* idx = 0);
string s1="3.1678";
n=stod(s1);
用法类似于stof
stold()
long double stold (const string& str, size_t* idx = 0);
long double stold (const wstring& str, size_t* idx = 0);
用法类似于stof
运算符重载
所谓重载,就是赋予新的含义。函数重载可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载也是一个道理,同一个运算符可以有不同的功能。
格式:
返回值类型 operator 运算符名称 (形参表列){
//TODO:
}
class complex{
private:
int L; //实部
int R; //虚部
public:
complex(int l, int r) {
L = l, R = r;
};
//声明运算符重载
int operator+(const complex &A) {
return L + A.L;
};
};
complex a(1, 2);
complex b(2, 3);
int sum = a + b; //sum = 3
结构体的构造函数
这里用二叉树的节点作为例子
struct TreeNode {
int val;
TreeNode l;
TreeNode r;
TreeNode(int x): val(x), l(NULL), r(NULL){}
};
交换两个变量
swap
使用它只需要using namespace std就好了,swap可以适用于各种类型
using namespace std;
int a = 0, b = 10;
swap(a, b);
异或^
异或法可以完成对整型变量的交换,对于浮点型变量它无法完成交换
int a = 0, b = 10;
a ^= b;
b ^= a;
a ^= b;
加减法
该方法可以交换整型和浮点型数值的变量,但在处理浮点型的时候有可能出现精度的损失
int a = 0, b = 10;
a = a + b;
b = a - b;
a = a - b;
乘除法
int a = 1, b = 10;
a = a * b;
b = a / b;
a = a / b;
乘除法更像是加减法向乘除运算的映射,它与加减法类似:可以处理整型和浮点型变量,但在处理浮点型变量时也存
在精度损失问题。而且乘除法比加减法要多一条约束:b必不为0
可能经验上的某种直觉告诉我们:加减法和乘除法可能会溢出,而且乘除的溢出会特别严重。其实不然,采用这两种
方法都不会溢出。以加减法为例,第一步的加运算可能会造成溢出,但它所造成的溢出会在后边的减运算中被溢出回来。
reverse翻转
使用之前需要引入头文件#include
该函数的功能是对指定范围内的值进行翻转。
语法reverse(begin, begin + n),n 是翻转元素的个数
翻转范围为[begin,begin + n),是左闭右开区间
#include<algorithm>
//翻转数组
int a[] = {1,2,3,4};
reverse(a, a + 2);
//翻转容器
vector<int> a = {1, 2, 3, 4};
reverse(a.begin(), a.end());
//字符串也可以像容器那样用
string str = "abcd";
reverse(str.begin(), str.end());
原码\反码\补码
- int等类型的数据,最高位为符号位,0为正,1为负
- 正数的原码\反码\补码一样
- 负数的反码是符号位不变,其余位取反
- 负数的补码是反码加一
大小端对齐
- 计算机中最小的内存单位是BYTE(字节) , 一个大于BYTE的数据类型在内存中存放的时候是有先后顺序的.
- **内存在划分地址是左高右低的,如果变量存储方式和内存划分方式一样那就是小端对齐模式,**也就是高内存地址放整数的高位,低内存地址存放整数的低位,这种方式叫做倒着放,术语叫做小端对齐, x86和ARM都是小端对齐的.
- 高内存地址存放整数的低位,低内存地址存放整数的高位,这种方式叫做正着放,术语叫做大端对其,很多unix服务器的CPU都是大端对齐的
联合体union
- 联合体是在同一个地址空间内,存不同类型数据
- 空间长度位类型中最长的
- 但是这个地方同时只能表示一个类型的数,修改其中一个成员,其他也会被修改
union student
{
int a;
char b;
short c;
int d;
};
union student s;
//s的长度就是4字节,
枚举类型enum
- 枚举类型是int型的整数常量
- 第一个是0,后面的依次加一
- 如果手动给某个成员幅值,那它后面的就在这基础上加一
enum sex {man, woman};
//sex.man就是int型的0,sex.woman是1
const和指针
const int* u;//u是一个指针,它指向一个const int,正指向的元素不发生改变
int const* u;//与上面一样
int d = 1;
int* const w = &d;//w是一个指针,这个指针是指向int的const指针
const指针是指针变量的值一经初始化,就不可以改变指向,初始化是必要的。其定义形式如下:
type *const 指针名称;
声明指针时,可以在类型前或后使用关键字const,也可在两个位置都使用。例如,下面都是合法的声明,但是含义大不同:
const int * p; //指向整形常量 的指针,它指向的值不能修改
int * const p; //指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。
const int *const p; //指向整形常量 的常量指针 。它既不能再指向别的常量,指向的值也不能修改。
const int a = 0;
const int* b = &a; //常量指针, *b = 1;错误
int c = 1;
int e = 2;
int* const d = &c; // 指针常量,d = &e;错误
理解这些声明的技巧在于,查看关键字const右边来确定什么被声明为常量 ,如果该关键字的右边是类型,则值是常量;如果关键字的右边是指针变量,则指针本身是常量
C++ 常量
常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。
常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
定义常量
在 C++ 中,有两种简单的定义常量的方式:
使用 #define 预处理器。
使用 const 关键字。
C++对象
-
构造函数:分为有参构造和无参构造,也可以分为普通构造和拷贝构造,一个类可以写多种构造函数,都可以用。即使自己不写,编译器也会自动给加上默认构造、默认析构、默认拷贝构造

-
拷贝构造函数:要求你不能改变原来的对象,所以需要时const的,还需要引用的方式传入。核心就在于拷贝,拷贝到是值
-
什么情况下会用拷贝构造:要把一个对象的值传递给另一个对象的时候,要把类通过参数值传递的方式作为传入参数的时候,要把类的值作为函数返回值的时候;






-
深拷贝与浅拷贝:
(1)浅拷贝:编译器给的默认拷贝构造就是浅拷贝,仅仅把值拷过去,在比如构造中需要新开辟一块内存的情况,如果只用浅拷贝,那它拷贝过去是原来对象的的那个地址值
(2)深拷贝:深拷贝就是不是仅仅拷贝值过去,它也会开辟一个内存,只是这块内存里面存的值和原来的对象的值一样


(3)在面对需要新开辟空间的的那种情况,浅拷贝会出问题,就是比如p2析构之后,那块地址也会被释放掉,但现在p1还没有析构,这就会出问题
5. 初始化列表

6. 对象作为类的属性:比如在person类中创建了一个phone对象作为自己的成员,当创建person类的时候,先进行的是phone的构造,再到person的构造,析构的时候,先析构person,再析构phone

如果作为类成员的那个类需要有参构造,那么它就必须放在初始化列表那里进行初始化构造
如果作为类成员的那个类不需要需要有参构造,那么它就可以不放在初始化列表那里
- **静态成员:**就是在变量或者函数前面加上static关键字
(1)静态成员变量:所有的对象共享同一块内存 ,不需要创建对象就可以使用,类内申明,类外初始化,在编译阶段就分配了内存。
可以通过类名直接访问,也可以通过对象访问
(2)静态成员函数:所有的对象共享同一个函数,只能访问静态成员变量(因为其他成员函数都是属于特定对象的,你去访问这个成员函数的话,它不知道是哪个对象的),也是两种调用方式,类名和对象 - 空对象占用空间1个字节,原因是为了区分这是一个空对象,如果是0的话,如果我创建了两个空对象,不能让他俩占在同一个内存地址呀

- this指针

- 友元:直白讲就是一个类有个朋友,这个朋友可以访问自己的私有成员




- 运算符重载:就是针对自定义的数据类型,如果用默认的运算符,编译器是不知道该怎么运算的,所以需要指明一下,自己给运算符定义一下操作

- 继承:

(1)子类继承了父类,父类的所有成员都被子类继承了,只是说有权限限制,有的可能访问不到(比如父类的private就访问不到)
(2)当子类继承父类之后,创建子类对象时,也会创建父类对象,并且父类对象在先构造,析构时候顺序是反过来
(3)子类继承父类之后,如果子类和父类有同名的成员
如果继承的那个类需要有参构造,那么它就必须放在初始化列表那里进行初始化构造,传入参数
如果继承的那个类不需要需要有参构造,那么它就可以不放在初始化列表那里


(4)继承多个父类

(5)菱形继承,比如有个动物类,有个养类、驼类,他俩都继承了动物类,有个羊驼类,它继承了羊类和驼类,这就是菱形继承
(6)菱形继承带来麻烦,有的成员多份重复了,比如动物类中的年龄,羊驼就拥有了两份,这样浪费
(7)为了避免浪费,可以引入虚继承,通过虚继承的基类叫做虚基类,通过虚继承之后,它们就是不是两份数据了,而是继承了vbptr(虚指针)指向基类中的数据
(8)多继承尽量少用


参考博客链接

13. 多态
(1)一个接口多种形态
(2)重写、重载就是多态的体现
(3)定义一个基类,写一个虚函数,里面没有具体的实现,然后用子类继承这个基类,在子类中重写虚函数,左具体的实现,不同的子类写不同的实现,在使用的时候,用父类指针或者引用来指向子类对象,这样当这个父类指针指向不同类的时候,调用的就是不同子类的方法,这就实现了一个接口,多个形态。
模板
- 模板定义






2. 模板函数,在不指明类型的时候,不可以发生隐式转换(自动类型转换),指定了类型之后,就可以发生隐式转换了

3. 普通函数和模板函数

4. 模板斌不是万能的,在通用的模板函数之外,为自定义的数据类型可以单独写一个专门的的模板函数 

5. 类模板

6. 类模板不能自动类型推导 ,需要指明类型,也可以在类模板这里使用默认参数类型


7. 普通类的成员函数在一开始的时候就会创建,类模板的成员函数只有在调用的时候才会创建
8. 如果父类式类模板,子类需要指出父类中T的数据类型
9. 类模板分文件编写

模板案例
template<typename T>
void printSize(T a){
cout << sizeof(T) << endl;
cout << sizeof(a) << endl;
}
template<typename T>
void printSizeRef(T&& a){
cout << sizeof(T) << endl;
cout << sizeof(a) << endl;
}
int main(){
int a[100] ={0};
cout<<sizeof(a)<<endl;
printSize(a);
printSizeRef(a);
}
求在64位系统中的输出?
在64位Ubuntu服务器中,编译运行后输出的结果是:8,8,400,400,
也就是说:
第一个函数中的T和a的类型都是指针
第二个函数中的T和a的类型都是大小为100的int数组
C++模板类型推导要点:
按值传递给函数模板的数组类型将被推导为指针类型,原因是C语言中数组类型可以隐式类型转换为指向数组首元素的指针,C++的基础是C,所以允许这样的转换
在模板类型推导过程中,数组的实参在被用来初始化引用时,不会被退化为指针,此时形参的实际类型是数组的引用
容器
- string容器

- vector容器
动态扩展不是直接在后面添加,而是发现如果之前预留的空间不够大的话,找一块更大的地方,再把原来的复制过来再添加





- deque


由于他的原理和vector不同,所以他访问元素的效率不如vector,因为需要去中控器查询以下地址

内存模型
- 内存分区




- 不要放回局部变量的地址,下面例子中,第一次*p能获得值,第二次就是随机值了

- 下面的例子中,内存开辟在了堆区,他的地址是p来存,p是放在栈上的

- new开辟堆区内存,存放数组

引用
- 本质就是给变量取别名

- 注意事项
引用必须一开始就初始化
并且初始化后,就不能更改了(就是你做了a的别名,就不能再转去做c的别名了),因为他本质是指针常量


- 引用左函数返回值



- 引用的底层其实是指针常量


- 常量引用


函数高级功能
- 函数自带默认参数

- 占位参数

- 函数重载



正则表达式



char*中查找要用cmatch保存结果
cmatch cm;
char* str = "fagag123.255fsffg55.555sa";
regex pattern("[0-9]*\\.[0-9]*");
regex_search(str, cm, pattern);
for(auto c : cm) cout<<c<<endl;//由于只查找一次,所以只有一个
string中查找用smatch保存结果
smatch sm;
string str = "fagag123.255fsffg55.555sa";
regex pattern("[0-9]*\\.[0-9]*");
regex_search(str, sm, pattern);
for(auto s : sm) cout<<s<<endl;
连续查找所有的
string str = "1/2022-08-07T08:00:00/sfsftest/Te2021-08-07T08:00:00st1rwefs";
regex pattern("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}");
for (sregex_iterator it(str.begin(), str.end(), pattern), end_it; it != end_it; ++it) {
cout << it->str() << endl;
}

string mystr = "This is software testing Help portal and postcard";
cout<<"1: "<<mystr<<endl;
regex regexp("p[a-zA-z]+");
cout<<"2: "<<regex_replace(mystr, regexp, "website")<<endl;
cout<<"3: "<<mystr<<endl;
string result;
regex_replace(back_inserter(result), mystr.begin(), mystr.end(),
regexp, "website");
cout <<"4: "<< result;
//1: This is software testing Help portal and postcard
//2: This is software testing Help website and website
//3: This is software testing Help portal and postcard
//4: This is software testing Help website and website
这篇C++笔记涵盖了哈希表如unordered_set和unordered_map的使用,迭代器的原理,pair的组合功能,无参数构造函数的应用,以及printf和sprintf的格式化输出。还讨论了整数到二进制字符串的转换,字符串转为不同类型,如stoi、stol等函数的使用。此外,介绍了运算符重载、结构体构造函数、变量交换的不同方法,以及reverse函数用于数组或字符串翻转。最后,涉及了原码、反码、补码的概念,大小端对齐,联合体union,枚举类型enum,const和指针的交互,以及C++中的常量、对象、模板和容器的相关知识点。
4793

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



