一、C++入门基础
1.第一个C++程序
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
return 0;
}
C++中标准输入输出的头文件是<iostream>不需要加.h
2.命名空间
1.namespace的价值
比如有定义一个变量rand,但是头文件中同时已经存在了rand函数,这时就会报错
2.namespace的定义
namespace hiro//这个是空间名,可以自定义
{
int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}//这里不需要加 ;
namespace hiro//这个是空间名,可以自定义
{
namespace hiro_a
{
}
}
3.命名空间使⽤
// 指定命名空间访问int main(){printf("%d\n", N::a);return 0;}// using将命名空间中某个成员展开using N::b;int main(){printf("%d\n", N::a);printf("%d\n", b);return 0;}// 展开命名空间中全部成员using namespce N;int main(){printf("%d\n", a);printf("%d\n", b);return 0;}
用 "::"来进行引用
3.C++输入和输出
// 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码// 可以提⾼C++IO效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
4.缺省参数
//全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a= " << a << endl;
cout << "b= " << b << endl;
cout << "c= " << c << endl<<endl;
}
//半缺省
void Func2(int a, int b = 20, int c = 30)
{
cout << "a= " << a << endl;
cout << "b= " << b << endl;
cout << "c= " << c << endl<<endl;
}
//void Func2(int a=10, int b = , int c = 30)不能这么给形参
int main()
{
Func1(1, 2, 3);
Func1();
Func2(1,2,3);
Func2(1, 2);
Func2(1);
//Func1(1,3)不能这样跳着给实参
}
5.函数重载
//函数重载
//1.参数类型不同
int ADD(int left, int right)
{
cout << "int ADD(int left, int right)" << endl;
return left + right;
}
double ADD(double left, double right)
{
cout << "double ADD(double left, double right)" << endl;
return left + right;
}//函数类型不同不可以
//是重载但是调用有问题
void f1()
{
return;
}
void f1(int a = 10)
{
return;
}
//可以这样
namespace hiro
{
void f2()
{
return;
}
}
void f2(int a = 10);
//3.参数顺序不同
void f4(int a, int b)
{
cout << "f(int a,char b)" << endl;
}
void f4(int b, int a)
{
cout << "f(int b,char a)" << endl;
}
6.引用
1.引⽤的概念和定义

类型& 引⽤别名 = 引⽤对象;
//引用
int main()
{
int m = 0;
int& n = m;
int& p = m;
p++;
cout << p << endl;
n++;
cout << m << endl<<endl;
}
2.引用的特性
int main()
{
int a = 10;
// 编译报错:“ra”: 必须初始化引⽤
//int& ra;
int& b = a;
int c = 20;
// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,
// 这⾥是⼀个赋值
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}
3.引⽤的使⽤
void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}
int main()
{
int x = 0, y = 1;
cout << x <<" " << y << endl;
Swap(x, y);
cout << x << " " << y << endl;
return 0;
}
比如写链表时就可以利用到引用:
typedef struct ListNode
{
int val;
struct ListNode* next;
}LTNode, * PNode;
// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{
PNode newnode = (PNode)malloc(sizeof(LTNode));
newnode->val = x;
newnode->next = NULL;
if (phead == NULL)
{
phead = newnode;
{
//...
}
}
栈中
int& STTop(ST& rs)
{
assert(rs.top > 0);
return rs.a[rs.top];//返回一个引用,引用的名字不重要
}
4.const引用
产生临时对象的情况:
1.函数参数或返回值是值类型(而非引用 / 指针)时,编译器会创建临时对象来拷贝数据
2. 隐式类型转换
当表达式中需要某种类型,但提供的是另一种可转换的类型时,编译器会创建临时对象完成转换
3. 表达式运算中的临时对象
5.指针和引⽤的关系
6.inline


#include<iostream>
using namespace std;
inline int Add(int x, int y)
{
int ret = x + y;
ret += 1;
ret += 1;
ret += 1;
return ret;
}
int main()
{
// 可以通过汇编观察程序是否展开
// 有call Add语句就是没有展开,没有就是展开了
int ret = Add(1, 2);
cout << Add(1, 2) * 5 << endl;
return 0;
}
7.nullptr
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif#endif
#include<iostream>
using namespace std;
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
// 本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int
x),因此与程序的初衷相悖。
f(NULL);
f((int*)NULL);
// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
// f((void*)NULL);
f(nullptr);
}
二、类和对象
1.类的定义
1.类定义格式
2.访问限定符
3.类域
#include<iostream>
using namespace std;
class Stack
{
public:
// 成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}
2.实例化
1.实例化概念

class Date
{
public:
Date(int year = 2026, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;//这里只是声明,并没有开辟空间
};
int main()
{
// Date类实例化出对象d1和d2
Date d1(2026,2,1);
Date d2(2026,4,3);
}
2.对象⼤⼩
函数不占对象的大小,函数存放在代码段
函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在 运⾏时找,就需要存储函数地址
// 内部嵌套结构体
struct Inner {
char a; // 大小1,对齐数min(1,8)=1
int b; // 大小4,对齐数min(4,8)=4
double c; // 大小8,对齐数min(8,8)=8
};
// 外部结构体
struct Outer {
char x; // 大小1,对齐数1
Inner inner; // 嵌套结构体,先算Inner的属性
short y; // 大小2,对齐数min(2,8)=2
};
Inner最大对齐数:8 | Inner总大小:16
Outer最大对齐数:8 | Outer总大小:32
没有任何成员的类的大小是1,纯粹是为了占位标识对象存在
3.this指针
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
// 编译报错:error C2106: “=”: 左操作数必须为左值
// this = nullptr;
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;//这里可以加上this->,但是没必要
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
// d1.Init(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();//这里不能写d1.Print(&d1);
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
//this指针理论上会放到栈里,vs放到寄存器
4.C++和C语⾔实现Stack对⽐
C++实现栈的代码
#include<iostream>
#include<cassert>
using namespace std;
typedef int STDataType;
class Stack
{
public:
// 成员函数
//构造函数
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
//析构函数
//后生成的先析构
~Stack()
{
free(_a);
_a = nullptr;
_capacity = _top = 0;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
int Top()
{
assert(_top > 0);
return _a[_top - 1];
}
void Destroy()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
// 成员变量
STDataType* _a;
size_t _capacity;
size_t _top;
};
3. 类的默认成员函数

5.构造函数
class Date
{
public:
// 1.⽆参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
// 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
= day;
}
// 3.全缺省构造函数
/*Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}*/
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 如果留下三个构造中的第⼆个带参构造,第⼀个和第三个注释掉
// 编译报错:error C2512: “Date”: 没有合适的默认构造函数可⽤
Date d1; // 调⽤默认构造函数
Date d2(2025, 1, 1); // 调⽤带参的构造函数
// 注意:如果通过⽆参构造函数创建对象时,对象后⾯不⽤跟括号,否则编译器⽆法
// 区分这⾥是函数声明还是实例化对象
// warning C4930: “Date d3(void)”: 未调⽤原型函数(是否是有意⽤变量定义的?)
Date d3();
d1.Print();
d2.Print();
return 0;
}
6.析构函数
7.拷⻉构造函数

传引用的时候没有进行传值,只有拷贝,所以不会无穷递归;传值传参的时候需要进行拷贝构造,而这里的拷贝构造需要传值传参,所以造成了无穷递归
void Func1(Date d)
{
cout << &d << endl;
d.Print();
}
Date& Func2()
{
Date tmp(2024, 7, 5);
tmp.Print();
return tmp;
}
int main()
{
Date d1(2024, 7, 5);
// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥传值传参要调⽤拷⻉
构造
// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉,传引⽤传参可以较少这⾥的拷⻉
Func1(d1);
cout << &d1 << endl;
// 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造
Date d2(&d1);
d1.Print();
d2.Print();
//这样写才是拷⻉构造,通过同类型的对象初始化构造,⽽不是指针
Date d3(d1);
d2.Print();
// 也可以这样写,这⾥也是拷⻉构造
Date d4 = d1;
d2.Print();
// Func2返回了⼀个局部对象tmp的引⽤作为返回值
// Func2函数结束,tmp对象就销毁了,相当于了⼀个野引⽤
Date ret = Func2();
ret.Print();
return 0;
}
// 两个Stack实现队列
class MyQueue
{
public:
private:
Stack pushst;
Stack popst;
};
int main()
{
Stack st1;
st1.Push(1);
st1.Push(2);
// Stack不显⽰实现拷⻉构造,⽤⾃动⽣成的拷⻉构造完成浅拷⻉
// 会导致st1和st2⾥⾯的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃
Stack st2 = st1;
MyQueue mq1;
// MyQueue⾃动⽣成的拷⻉构造,会⾃动调⽤Stack拷⻉构造完成pushst/popst
// 的拷⻉,只要Stack拷⻉构造⾃⼰实现了深拷⻉,他就没问题
MyQueue mq2 = mq1;
return 0;
}
8.赋值运算符重载
1.运算符重载
Date operator++(int)
{
Date tmp(*this);
*this += 1;return tmp;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//cout从左往右调用,所以这里要返回out来继续插入下一个值
return out;
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请输出年月日:";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "输入非法日期,请重新输入" << endl;
}
else
{
break;
}
}
return in;
}
2.赋值运算符重载
9.取地址运算符重载
1.const成员函数
void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
2.取地址运算符重载
10.再探构造函数(初始化列表)
class Date
{
public:
Date(int year, int month, int day)
:_year(year)//初始化列表
,_month(month)//括号里可以放初始值,也可以放表达式
,_day(day)
//,x(1)//const类型的初始化要用初始化列表
//,y(day)//引用也要
//,_time(1)//没有默认构造的自定义类型必须用
//,_ptr((int*)malloc(sizeof(int)*5))//可以和函数混着用,比如这样
////,_year(year)每个成员变量只能出现一次,像这样在写一次就不行
{
//if (_ptr == NULL)
//{
// perror("malloc fail");
// return;
//}
//else
//{
//}
}
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//声明
int _year=1;
int _month=1;
int _day=1;
//int* _ptr=(int*)malloc(sizeof(int)*5);//这种也可以给缺省值
//Time _time=1;//这里也是
////必须在初始化列表初始化
//const int x;
//int& y;
};

11. 类型转换
#include<iostream>
using namespace std;
class A
{
public:
// 构造函数explicit就不再⽀持隐式类型转换
// explicit A(int a1)
A(int a1)
:_a1(a1)
{}
//explicit A(int a1, int a2)
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
int Get() const
{
return _a1 + _a2;
}
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
public:
B(const A& a)
:_b(a.Get())
{}
private:
int _b = 0;
};
int main()
{
// 1构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造aa3
// 编译器遇到连续构造+拷⻉构造->优化为直接构造
A aa1 = 1;
aa1.Print();
const A& aa2 = 1;
// C++11之后才⽀持多参数转化
A aa3 = { 2,2 };
// aa3隐式类型转换为b对象
// 原理跟上⾯类似
B b = aa3;
const B& rb = aa3;
return 0;
}
12. static成员
#include<iostream>
using namespace std;
class A
{
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{
return _scount;
}
private:
// 类⾥⾯声明
static int _scount;
};
// 类外⾯初始化
int A::_scount = 0;
13. 友元
#include<iostream>
using namespace std;
// 前置声明,否则A的友元函数声明编译器不认识B
class B;
class A
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _b1 = 3;
int _b2 = 4;
};
void func(const A& aa, const B& bb)
{
cout << aa._a1 << endl;
cout << bb._b1 << endl;
}
int main()
{
A aa;
B bb;
func(aa, bb);
return 0;
}
class A
{
// 友元声明
friend class B;
private:
int _a1 = 1;
int _a2 = 2;
};
14.内部类
15. 匿名对象
// 匿名对象在这样场景下就很好⽤,当然还有⼀些其他使⽤场景,这个我们以后遇到了再说class Solution {public:int Sum_Solution(int n) {//...return n;}};Solution().Sum_Solution(10);
16.对象拷⻉时的编译器优化

三、内存管理
1. C/C++内存分布

2.C++内存管理方式
1. new/delete操作内置类型
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}

2.new和delete操作自定义类型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
还会调用构造函数和析构函数
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A)*10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}
在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
3.operator new与operator delete函数
new是先调用malloc再调用构造函数,delete先调用析构再调用free
4.new和delete的实现原理
1.内置类型
2.自定义类型
5.定位new表达式(placement-new)
new (place_address) type//或者new (place_address) type(initializer-list)
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
有执行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}
6.malloc/free和new/delete的区别
补充:new[]申请数组时,时如果对象中有非平凡析构的成员,那么会在头部多开4/8字节用来存放数组的长度
四、模板
1. 泛型编程
在想要实现一个通用的函数时,可以通过函数重载,但是这样太麻烦,要写很多个,代码复用率很低,这时就可以使用函数模板来解决
2. 函数模板
1.函数模板概念
2.函数模板格式
template<typename T1, typename T2,......,typename Tn>返回值类型函数名(参数列表){}
template<typename T> //这里的typename也可以用class,效果都一样void Swap( T& left, T& right){T temp = left;left = right;right = temp;}
3.函数模板的原理

4.函数模板的实例化
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2);
Add(d1, d2);
/*
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有
一个T,
编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要
背黑锅
Add(a1, d1);
*/
// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
Add(a, (int)d);
return 0;
}
int main(void)
{
int a = 10;
double b = 20.0;
// 显式实例化
Add<int>(a, b);
return 0;
}
5.模板参数的匹配原则
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化,这里调用的是非模板函数
Add<int>(1, 2); // 调用编译器特化的Add版本 ,这里<>强制编译器使用函数模板
}
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的
Add函数
}
3. 类模板
1 .类模板的定义格式
template<class T1, class T2, ..., class Tn>class 类模板名{// 类内成员定义};
#include<iostream>
using namespace std;
// 类模版
template<typename T>
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = new T[capacity];
_capacity = capacity;
_size = 0;
}
void Push(const T& data);
private:
T* _array;
size_t _capacity;
size_t _size;
};
// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
template<class T>
void Stack<T>::Push(const T& data)
{
// 扩容
_array[_size] = data;
++_size;
}
int main()
{
Stack<int> st1; // int
Stack<double> st2; // double
return 0;
}
2.类模板的实例化
// Stack是类名,Stack<int>才是类型Stack<int> st1; // intStack<double> st2; // double
4.非类型模板参数
template<class T, size_t N = 10>
5.模板的特化
1.概念
template<class T>
bool Less(T left, T right)
{
return left < right;
}
int main()
{
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 可以比较,结果错误
return 0;
}
2.函数模板特化
template<class T>bool Less(T left, T right){return left < right;}// 对Less函数模板进行特化template<>bool Less<Date*>(Date* left, Date* right){return *left < *right;}
3. 类模板特化
1 .全特化
template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};template<>class Data<int, char>{public:Data() {cout<<"Data<int, char>" <<endl;}private:int _d1;char _d2;};
2.偏特化
1.部分特化
template <class T1>class Data<T1, int>{public:Data() {cout<<"Data<T1, int>" <<endl;}private:T1 _d1;int _d2;};
2.参数更进一步的限制
//两个参数偏特化为指针类型template <typename T1, typename T2>class Data <T1*, T2*> //这里有<>所以是特化{public:Data() {cout<<"Data<T1*, T2*>" <<endl;}private:T1 _d1;T2 _d2;};//两个参数偏特化为引用类型template <typename T1, typename T2>class Data <T1&, T2&>{public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout<<"Data<T1&, T2&>" <<endl;}private:const T1 & _d1;const T2 & _d2;};//两个参数偏特化为指针类型template <typename T1, typename T2>class Data <T1*, T2*>{public:Data() {cout<<"Data<T1*, T2*>" <<endl;}private:T1 _d1;T2 _d2;};//两个参数偏特化为引用类型template <typename T1, typename T2>class Data <T1&, T2&>{public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout<<"Data<T1&, T2&>" <<endl;}private:const T1 & _d1;const T2 & _d2;};
3.类模板特化应用示例
template<class T>
struct Less
{
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
int main()
{
Date d1(2022, 7, 7);
Date d2(2022, 7, 6);
Date d3(2022, 7, 8);
vector<Date> v1;
v1.push_back(d1);
v1.push_back(d2);
v1.push_back(d3);
// 可以直接排序,结果是日期升序
sort(v1.begin(), v1.end(), Less<Date>());
vector<Date*> v2;
v2.push_back(&d1);
v2.push_back(&d2);
v2.push_back(&d3);
// 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
// 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
// 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
sort(v2.begin(), v2.end(), Less<Date*>());
return 0;
}
// 对Less类模板按照指针方式特化template<>struct Less<Date*>{bool operator()(Date* x, Date* y) const{return *x < *y;}};
6.模板分离编译
1.什么是分离编译
2.模板的分离编译
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}

3.解决方法
五、STL
1.STL介绍
STL的六大组件:

2.string类
string提供了对字符串的各种操作函数
1.auto和范围for
1.auto
2.范围for
for (auto& e : array)e *= 2;for (auto e : array)cout << e << " " << endl;
2.vs和g++下string结构的说明
1.vs
2.g++下string的结构
3.string各种接口的使用
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
//class string //operator[]大概就是这样
//{
//public:
// char& operator[](size_t i)
// {
// assert(i<_size)
// return _str[i]; //这里返回第i个字符的引用,就可以用类似 s5[3] = 'x'; 来改变第i个位置的字符
// }
//private:
// char* _str;
// size_t _size;
// size_t _capacity;
//};
int main()
{
string s1;
string s2("hello world");
string s3(s2);
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
string s4(s2, 6, 5);//从第n个位置拷贝m个字符
cout << s4 << endl;
string s5(s2, 6, 15);//这样会直接拷贝到最后,不会报错
string s6(s2, 6 /*,nops*/ );//这里会直接相当于传npos,npos=-1 但是size_t类型是无符号的
//所以这里会直接给最大值42亿多,也就相当于直接拷贝到最后
string s7("hello world", 5); //拷贝前n个字符
string s8(10, 'X');//拷贝n个字符到字符串中,这里必须是 ''
cout << s7 << endl;
cout << s8 << endl;
s5[3] = 'x'; //和数组一样改变字符串第i个位置的字符, 越界了还会直接报错
cout << s5 << endl;
for (int i = 0; i < s2.size(); i++) //string中提供了一个size接口来获得字符串的从长度
{
cout << s2[i] << " "; //这种方法只有底层是数组的时候可以,其他类型的时候就要用迭代器了
}
cout << endl;
//必须指明类域
string::iterator it = s2.begin(); //这里用了迭代器,it可以看作是一个指针,
//如果it不是指针,这里的运算符也可以用运算符重载来进行操作
while (it != s2.end())//这里的end()返回的是 past the end ,就是end的下一个位置
{
*it += 2;//迭代器这里可以修改s2里的字符
cout << *it << " ";
it++;
}
cout << endl;
//遍历的另一种方法
//范围for 这个适用于数组和容器
for (auto ch : s2) //这里的auto是自动推导,就是自动推导这里需要的类型
//这里可以写成 char chr:s2
//底层就是迭代器,这里编译的时候会变成上面迭代器的样子
//但是上面的迭代器可以修改,这里的ch只是拷贝
{
ch-=2; //这里只能修改ch的值,s2不会变
cout << ch << " ";
}
cout << endl;
cout << s2 << endl;
for (auto& ch : s2) //想要修改的话需要把这里改成引用,这里ch就变成了s2里每个字符的引用
{
ch-=2;
cout << ch << " ";
}
cout << endl;
int a = 0;
cout << typeid(s2).name() << endl; //typeid可以用来看类型
cout << typeid(a).name() << endl;
//auto e; //必须有初值,auto是通过右边的初值来推导类型的
//auto a = 1, b = 1.1; //这样会报错,必须是同一种类型
//auto a[]={4,5,6} //不能用来定义数组
//int func1(auto a) //不能做参数
//auto func2(int a) // 但是可以做返回值
//反向迭代器 就是把迭代器全都倒过来了
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit << " ";
rit++; //++也会倒着走
}
cout << endl;
const string s9("hello world"); //这里是const auto会调用最匹配的 也就是const迭代器
//const迭代器 //只能读不能写 cit指向的内容不能被修改
//string::const_iterator cit = s9.begin();
auto cit = s9.begin();
while (cit != s9.end())
{
cout << *cit << " ";
cit++;
}
cout << endl;
//const反向迭代器
//string::const_reverse_iterator crit = s9.rbegin();
auto crit = s9.rbegin();
while (crit != s9.rend())
{
cout << *crit << " ";
crit++;
}
cout << endl;
//string的长度/数据个数
cout << s2.size() << endl;
cout << s2.length() << endl;
cout << s2.max_size() << endl;//string最大能开多长
cout << s2.capacity() << endl;//有效空间
s1.reserve(100); //预留n个字节的空间 大小不包含'\0'
s1.reserve(10); //这里会不会再缩小空间不一定,但是对内容肯定不会有影响
//也就是不会缩的比size还小
//vs这里不会缩小 linux里的g++会缩小
cout << s1.empty() << endl;
s1.shrink_to_fit(); //会把多余的内存还给系统
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Solution
{
public:
bool isLetter(char ch)
{
if (ch >= 'a' && ch <= 'z')
return true;
if (ch >= 'A' && ch <= 'Z')
return true;
return false;
}
string reverseOnlyLetters(string s)
{
int left = 0, right = s.size() - 1;
while (left < right)
{
while (left < right && !isLetter(s[left]))
{
++left;
}
while (left < right && !isLetter(s[right]))
{
--right;
}
swap(s[left++], s[right--]);
}
return s;
}
int firstUniqChar(string s) {
int count[26] = { 0 };
for (auto ch : s)
{
count[ch - 'a']++;
}
for (int i = 0; i < s.size(); ++i)
{
if (count[s[i] - 'a'] == 1)
return i;
}
return -1;
}
string addStrings(string num1, string num2) {
int end1 = num1.size() - 1, end2 = num2.size() - 1;
int next = 0;
string tmp;
while (end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
int ret = val1 + val2 + next;
next = ret / 10;
ret = ret % 10;
tmp.insert(tmp.begin(), ret + '0');
}
if (next)
{
tmp.insert(tmp.begin(), '1');
}
return tmp;
}
};
void test1()
{
string s1 = "hello world";
s1.push_back(' ');
s1.push_back('x');//尾插字符
s1.append("yyyy");//尾插字符串
s1 += "bbb";//这样的尾插更好用
cout << s1 << endl;
s1.insert(0,"aa");//头插//在第n个位置插入
s1.insert(0, 1, 'a');//插入一个需要这么写
//s1.insert(0,'a'); //这样写不行,没有提供这种写法
cout << s1 << endl;
s1.erase(5, 1);//在第a个位置开始删除b个字符
cout << s1 << endl;
s1.erase(s1.begin());//头删
cout << s1 << endl;
s1.erase(s1.end()--);//尾删
cout << s1 << endl;
s1.erase(6); //第n个后面全删
cout << s1 << endl;
s1.replace(4, 1, "&");
cout << s1 << endl;
//把s2里的所有空格都换成%
string s2 = "hello world 11 222 333 ";
cout << s2 << endl;
size_t pos = s2.find(' ');//在s2中找到空格的位置并返回其索引
while (pos != string::npos)
{
s2.replace(pos, 1, "%%");
pos = s2.find(' ',pos + 2);//后面可以加上寻找开始的位置
}
cout << s2 << endl;
auto str1 = s2.c_str(); //输出s2字符串的指针,这是用来兼容c的
//比如
//string file;
//cin >> file;
//FILE* fout = fopen(file.c_str(), "r");
//char ch=fgetc(fout);
//while (ch != EOF)
//{
// cout << ch;
// ch = fgetc(fout);
//}
//fclose(fout);
string str3 = "hello world abc";
size_t pos2 = str3.find(" ");
//size_t pos2 = str3.rfind(" "); 如果是找abc就可以这样
string suffix ="1111111111111111";
suffix = str3.substr(pos2); //用s3从pos位置往后的内容覆盖suffix
cout << suffix << endl;
}
int main()
{
string s1 = "hello world";
s1.resize(20,'x'); //会扩容,也会缩小
s1.resize(20); //没有指定的时候会把开的空间设置成 '\0'
/* Solution sol;
string test1 = "a-bC-dEf-ghIj";
string res1 = sol.reverseOnlyLetters(test1);
cout << "测试用例1:" << test1 << endl;
cout << "预期结果:j-Ih-gfE-dCba-" << endl;
cout << "实际结果:" << res1 << endl << endl;*/
test1();
return 0;
}
4.一些string类成员函数的实现:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace hiro
{
class string
{
public:
string()
:_str(new char[1]{'\0'})
,_size(0)
,_capacity(0)
{ }
string(const char* str)
{
_size = strlen(str);
//capacity不包含 \0
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
size_t size()
{
return _size;
}
char* begin()
{
return _str;
}
char* end()
{
return _str + _size;
}
size_t size()const
{
return _size;
}
const char* begin()const
{
return _str;
}
const char* end()const
{
return _str + _size;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
~string()
{
free(_str);
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
void test_string1();
}
3.vector
就是数组

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<vector>
using namespace std;
vector<vector<int>> generate(int numRows) { //创建一个杨辉三角
vector<vector<int>> vv(numRows);
for (int i = 0; i < vv.size(); ++i)
{
vv[i].resize(i + 1, 1);
}
for (int i = 2; i < vv.size(); ++i)
{
for (int j = 1; j < vv[i].size() - 1; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
return vv;
}
void TestVectorExpand()
{
size_t sz;
vector<int> v;
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (sz != v.capacity())
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
void vector_test1()
{
vector<int> v1; //顺序表的初始化
vector<int> v2(10,1); //给10个1
vector<int> v3(++v2.begin(),--v2.end()); //用v2的数据来初始化
for (size_t i = 0; i < v3.size(); i++)
{
cout << v3[i] << " ";
}
cout << endl;
vector<int>::iterator it = v3.begin();
while(it != v3.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto a : v3)
{
cout << a<<" ";
}
cout << endl;
}
void vector_test2()
{
vector<int> v1(10, 1);
v1.reserve(20);
cout << v1.capacity() << endl;
cout << v1.size() << endl;
v1.reserve(15);
cout << v1.capacity() << endl;
cout << v1.size() << endl;
v1.reserve(5);
cout << v1.capacity() << endl; //不会缩容
cout << v1.size() << endl;
cout << endl;
v1.resize(20);
cout << v1.capacity() << endl;
cout << v1.size() << endl;
v1.resize(15);
cout << v1.capacity() << endl;
cout << v1.size() << endl;
v1.resize(5);
cout << v1.capacity() << endl; //不会缩容但是会改变size
cout << v1.size() << endl; //数据多的话会删除数据
}
void vector_test3()
{
vector<int> v(10, 1);
//v.push_back(5);
//v.insert(v.begin(), 2); //必须用迭代器的形式
//for (auto e : v)
//{
// cout << e << " ";
//}
//cout << endl;
//v.insert(v.begin() + 3, 10); //可以直接这样来在某个位置插入
//for (auto e : v)
//{
// cout << e << " ";
//}
//cout << endl;
//vector<string> v2; //还可以用vector存储其他的自定义类型
//string s1("xxxx");
//v2.push_back(s1);
//v2.push_back("yyyy");
//for (const auto& e : v) //这里不加引用的话要多次拷贝构造
//{
// cout << e << " ";
//}
//cout << endl;
//cout << endl;
vector<vector<int>> vv(5, v); //相当于二维数组 一个10行5列的vector
vv[1][2] = 3;
vv.operator[](1).operator[](2) = 3; //上面的实际上是这样的代码
//operator传的是引用,这样才能对内容进行访问
for (auto ee : vv)
{
for (auto e : ee)
{
cout << e << " ";
}
cout << endl;
}
cout << endl;
cout << endl;
}
int main()
{
//TestVectorExpand(); //这里可以看出来vs下的vector是1.5倍扩容,和string一样
//vector_test1();
vector_test3();
return 0;
}
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace hiro
{
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
T* tmp = new T[n];
memcpy(tmp, _start, size() * sizeof(T));
delete[] _start;
_start = tmp;
_finish = tmp + old_size;
_end_of_storage = tmp + n;
}
}
size_t capacity()
{
return _end_of_storage - _start;
}
size_t size()
{
return _finish - _start;
}
size_t capacity()const
{
return _end_of_storage - _start;
}
size_t size()const
{
return _finish - _start;
}
void push_back(const T& x)
{
//扩容
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
bool empty()
{
return _start == _finish;
}
void pop_back()
{
assert(!empty());
--_finish;
}
iterator insert(iterator pos, const T& x) //这里pos前面不能加& 传过来的是临时变量的时候,具有常性,就不能用了
{
//扩容
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = x;
++_finish;
return pos;
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
/*void Print_Vector(const vector<int>& v)
{
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}*/
template<class T>
void Print_Vector(const vector<T>& v)
{
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
//在没有实例化的类模板里取东西的时候
//编译器不知道这里的const_iterator是类型还是静态成员变量
//这里加上typename是在告诉编译器这里是一个类型
//typename vector<T>::const_iterator it = v.begin();
auto it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test1()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
Print_Vector(v1);
vector<double> v2;
v2.push_back(1.1);
v2.push_back(2.2);
v2.push_back(3.3);
Print_Vector(v2);
v2.insert(v2.begin(), 10.2);
Print_Vector(v2);
int x;
cin >> x;
auto pos = find(v1.begin(), v1.end(), x);
if (pos != v1.end())
{
//insert之后pos就失效了,不能使用
pos=v1.insert(pos, 30);
//*pos *= 10;
pos = v1.insert(pos, 100); //前面的insert加上返回值之后就可以先更新以下pos再访问
*(pos + 2) *= 10;
}
Print_Vector(v1);
}
}
4.list
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
void test1()
{
list<int>lt;
lt.push_back(1);
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(4);
lt.push_back(4);
lt.push_back(5);
lt.push_back(1);
lt.push_back(3);
list<int>::iterator it = lt.begin();
while (it!=lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto i : lt)
{
cout << i << " ";
}
cout << endl;
lt.sort();
lt.unique();//去重 但是要求链表有序
for (auto i : lt)
{
cout << i << " ";
}
cout << endl;
lt.remove(1); //删除某个值
for (auto i : lt)
{
cout << i << " ";
}
cout << endl;
}
struct A
{
public:
A(int a1=1,int a2=2)
:_a1(a1)
,_a2(a2)
{ }
private:
int _a1;
int _a2;
};
void test2()
{
list<A> lt;
A aa1;
lt.push_back(aa1);
lt.push_back(A(3,3));
//支持直接传构造A的参数
lt.emplace_back(3, 3); //emplace_back还可以这样赋值
}
int main()
{
test1();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
namespace hiro
{
template <class T>
struct list_node
{
list_node(const T& data=T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{}
T _data;
list_node<T>* _next;
list_node<T>* _prev;
};
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T> Self;
Node* _node;
list_iterator(Node * node)
:_node(node)
{}
T& operator*()
{
return _node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
};
template <class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T> iterator;
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
iterator begin()
{
/*iterator it(_head->next);
return it;*/
return _head->_next;
}
iterator end()
{
/*iterator it(_head);
return it;*/
return _head;
}
/*void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_next = _head;
_head->_prev = newnode;
newnode->_prev = tail;
++_size;
}*/
void push_back(const T& x)
{
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void insert(iterator pos,const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++size;
}
void erase(iterator pos)
{
assert(pos!=end())
Node* next = pos._node->_next;
Node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--size;
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
size_t size()
{
return _size;
}
bool empty()
{
return _size == 0;
}
private:
Node* _head;
size_t _size;
};
void test1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cassert>
using namespace std;
namespace hiro
{
template <class T>
struct list_node
{
list_node(const T& data = T())
:_data(data)
, _next(nullptr)
, _prev(nullptr)
{
}
T _data;
list_node<T>* _next;
list_node<T>* _prev;
};
template<class T,class REF,class PTR>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T,REF,PTR> Self;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
REF operator*()
{
return _node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
PTR operator->()
{
return &_node->_data;
}
};
/*template<class T>
struct const_list_iterator
{
typedef list_node<T> Node;
typedef const_list_iterator<T> Self;
Node* _node;
const_list_iterator(Node* node)
:_node(node)
{}
const T& operator*()const
{
return _node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s)const
{
return _node != s._node;
}
const T* operator->()const
{
return &_node->_data;
}
};*/
template <class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T,T&,T*> iterator;
typedef list_iterator<T,const T&,const T*> const_iterator;
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(const list<T>& lt)
{
empty_init();
for (auto e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
list<T>&operator=(list<T>& lt)
{
swap(lt);
return *this;
}
iterator begin()
{
/*iterator it(_head->next);
return it;*/
return _head->_next;
}
const_iterator end()const
{
/*iterator it(_head);
return it;*/
return _head;
}
const_iterator begin()const
{
/*iterator it(_head->next);
return it;*/
return _head->_next;
}
iterator end()
{
/*iterator it(_head);
return it;*/
return _head;
}
/*void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_next = _head;
_head->_prev = newnode;
newnode->_prev = tail;
++_size;
}*/
void push_back(const T& x)
{
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return newnode; //为了防止迭代器失效
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* next = pos._node->_next;
Node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return next;//为了防止迭代器失效
}
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
size_t size()const
{
return _size;
}
bool empty()const
{
return _size == 0;
}
private:
Node* _head;
size_t _size;
};
struct AA
{
int _a1 = 1;
int _a2 = 2;
};
ostream& operator<<(ostream& out, const AA& aa)
{
out << aa._a1 << " " << aa._a2;
return out;
}
template<class Container>
void Print_Container(const Container& v)
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<AA> lta;
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
list<AA>::iterator ita = lta.begin();
while (ita != lta.end())
{
cout << ita->_a1/*这里其实是应该是两个-> 省略了一个*/ <<" " << ita->_a2 << endl;
//cout << ita.operator->()->_a1 << ita->_a2 << endl;
ita++;
}
cout << endl;
Print_Container(lta);
}
}
5.stack 和queue
1.queue.h
#pragma once
#include<iostream>
namespace hiro
{
template <class T, class Container = list<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front()const
{
return _con.front();
}
const T& back()const
{
return _con.back();
}
size_t size()const
{
return _con.size();
}
bool empty()const
{
return _con.empty();
}
private:
Container _con;
};
}
2.stack.h
#pragma once
#include<iostream>
namespace hiro
{
template <class T,class Container = vector<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top()const
{
return _con.back();
}
size_t size()const
{
return _con.size();
}
bool empty()const
{
return _con.empty();
}
private:
Container _con;
};
}
3.priority_queue
#pragma once
#include <algorithm>
#include<vector>
namespace hiro
{
template<class T,class Container=vector<T>>
class priority_queue
{
public:
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
//默认大堆
if (_con[parent] < _con[child])
{
Swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void AdjustDown(int parent)
{
int child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child] < _con[child + 1])
{
child++;
}
if (_con[parent] < _con[child])
{
Swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void push(const T& x )
{
_con.push_back(x);
AdjustUp(_con.size()-1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
const T& top()const
{
return _con[0];
}
// 判空
bool empty() const
{
return _con.empty();
}
// 元素个数
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}

2090

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



