【C++笔记】

这篇C++笔记涵盖了哈希表如unordered_set和unordered_map的使用,迭代器的原理,pair的组合功能,无参数构造函数的应用,以及printf和sprintf的格式化输出。还讨论了整数到二进制字符串的转换,字符串转为不同类型,如stoi、stol等函数的使用。此外,介绍了运算符重载、结构体构造函数、变量交换的不同方法,以及reverse函数用于数组或字符串翻转。最后,涉及了原码、反码、补码的概念,大小端对齐,联合体union,枚举类型enum,const和指针的交互,以及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());

原码\反码\补码

  1. int等类型的数据,最高位为符号位,0为正,1为负
  2. 正数的原码\反码\补码一样
  3. 负数的反码是符号位不变,其余位取反
  4. 负数的补码是反码加一

大小端对齐

  1. 计算机中最小的内存单位是BYTE(字节) , 一个大于BYTE的数据类型在内存中存放的时候是有先后顺序的.
  2. **内存在划分地址是左高右低的,如果变量存储方式和内存划分方式一样那就是小端对齐模式,**也就是高内存地址放整数的高位,低内存地址存放整数的低位,这种方式叫做倒着放,术语叫做小端对齐, x86和ARM都是小端对齐的.
  3. 高内存地址存放整数的低位,低内存地址存放整数的高位,这种方式叫做正着放,术语叫做大端对其,很多unix服务器的CPU都是大端对齐的

联合体union

  1. 联合体是在同一个地址空间内,存不同类型数据
  2. 空间长度位类型中最长的
  3. 但是这个地方同时只能表示一个类型的数,修改其中一个成员,其他也会被修改
union student
{
	int a;
	char b;
	short c;
	int d;
};
union student s;
//s的长度就是4字节,

枚举类型enum

  1. 枚举类型是int型的整数常量
  2. 第一个是0,后面的依次加一
  3. 如果手动给某个成员幅值,那它后面的就在这基础上加一
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++对象

  1. 构造函数:分为有参构造和无参构造,也可以分为普通构造和拷贝构造,一个类可以写多种构造函数,都可以用。即使自己不写,编译器也会自动给加上默认构造、默认析构、默认拷贝构造
    在这里插入图片描述

  2. 拷贝构造函数:要求你不能改变原来的对象,所以需要时const的,还需要引用的方式传入。核心就在于拷贝,拷贝到是值

  3. 什么情况下会用拷贝构造:要把一个对象的值传递给另一个对象的时候,要把类通过参数值传递的方式作为传入参数的时候,要把类的值作为函数返回值的时候;
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
(3)在面对需要新开辟空间的的那种情况,浅拷贝会出问题,就是比如p2析构之后,那块地址也会被释放掉,但现在p1还没有析构,这就会出问题
5. 初始化列表
在这里插入图片描述
6. 对象作为类的属性:比如在person类中创建了一个phone对象作为自己的成员,当创建person类的时候,先进行的是phone的构造,再到person的构造,析构的时候,先析构person,再析构phone
在这里插入图片描述
如果作为类成员的那个类需要有参构造,那么它就必须放在初始化列表那里进行初始化构造
如果作为类成员的那个类不需要需要有参构造,那么它就可以不放在初始化列表那里

  1. **静态成员:**就是在变量或者函数前面加上static关键字
    (1)静态成员变量:所有的对象共享同一块内存 ,不需要创建对象就可以使用,类内申明,类外初始化,在编译阶段就分配了内存。
    可以通过类名直接访问,也可以通过对象访问
    (2)静态成员函数:所有的对象共享同一个函数,只能访问静态成员变量(因为其他成员函数都是属于特定对象的,你去访问这个成员函数的话,它不知道是哪个对象的),也是两种调用方式,类名和对象
  2. 空对象占用空间1个字节,原因是为了区分这是一个空对象,如果是0的话,如果我创建了两个空对象,不能让他俩占在同一个内存地址呀
    在这里插入图片描述
  3. this指针
    在这里插入图片描述
  4. 友元:直白讲就是一个类有个朋友,这个朋友可以访问自己的私有成员
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. 运算符重载:就是针对自定义的数据类型,如果用默认的运算符,编译器是不知道该怎么运算的,所以需要指明一下,自己给运算符定义一下操作
    在这里插入图片描述
  6. 继承
    在这里插入图片描述
    (1)子类继承了父类,父类的所有成员都被子类继承了,只是说有权限限制,有的可能访问不到(比如父类的private就访问不到)
    (2)当子类继承父类之后,创建子类对象时,也会创建父类对象,并且父类对象在先构造,析构时候顺序是反过来
    (3)子类继承父类之后,如果子类和父类有同名的成员

如果继承的那个类需要有参构造,那么它就必须放在初始化列表那里进行初始化构造,传入参数
如果继承的那个类不需要需要有参构造,那么它就可以不放在初始化列表那里
在这里插入图片描述
在这里插入图片描述
(4)继承多个父类
在这里插入图片描述
(5)菱形继承,比如有个动物类,有个养类、驼类,他俩都继承了动物类,有个羊驼类,它继承了羊类和驼类,这就是菱形继承
(6)菱形继承带来麻烦,有的成员多份重复了,比如动物类中的年龄,羊驼就拥有了两份,这样浪费
(7)为了避免浪费,可以引入虚继承,通过虚继承的基类叫做虚基类,通过虚继承之后,它们就是不是两份数据了,而是继承了vbptr(虚指针)指向基类中的数据
(8)多继承尽量少用
在这里插入图片描述
在这里插入图片描述

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

模板

  1. 模板定义
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
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,所以允许这样的转换
在模板类型推导过程中,数组的实参在被用来初始化引用时,不会被退化为指针,此时形参的实际类型是数组的引用

容器

  1. string容器
    在这里插入图片描述
  2. vector容器
    动态扩展不是直接在后面添加,而是发现如果之前预留的空间不够大的话,找一块更大的地方,再把原来的复制过来再添加
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. deque
    在这里插入图片描述
    在这里插入图片描述
    由于他的原理和vector不同,所以他访问元素的效率不如vector,因为需要去中控器查询以下地址
    在这里插入图片描述

内存模型

  1. 内存分区
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 不要放回局部变量的地址,下面例子中,第一次*p能获得值,第二次就是随机值了
    在这里插入图片描述
  3. 下面的例子中,内存开辟在了堆区,他的地址是p来存,p是放在栈上的
    在这里插入图片描述
  4. new开辟堆区内存,存放数组
    在这里插入图片描述

引用

  1. 本质就是给变量取别名
    在这里插入图片描述
  2. 注意事项
    引用必须一开始就初始化
    并且初始化后,就不能更改了(就是你做了a的别名,就不能再转去做c的别名了),因为他本质是指针常量
    在这里插入图片描述
    在这里插入图片描述
  3. 引用左函数返回值
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  4. 引用的底层其实是指针常量
    在这里插入图片描述
    在这里插入图片描述
  5. 常量引用
    在这里插入图片描述
    在这里插入图片描述

函数高级功能

  1. 函数自带默认参数
    在这里插入图片描述
  2. 占位参数
    在这里插入图片描述
  3. 函数重载
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

正则表达式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值