0%

C++

函数模板

#include<iostream>
using namespace std;

//两个整型交换函数
void swapInt(int &a,int &b){
int temp = a;
a = b;
b = temp;
}

//两个浮点型交换函数
void swapDouble(double &a,double &b){
int temp = a;
a = b;
b = temp;
}

//函数模板
template<class T> //声明一个模板,告诉编译器后面紧跟着的代码中的T不要报错,T是一个通用数据类型。
void mySwap(T &a,T &b){
T temp = a;
a = b;
b = temp;
}

int main(){
int a=10;
int b=20;
//两种方式使用函数模板 1.自动类型推导:mySwap(a,b) 2.显示指定类型:mySwap<int>(a,b)
mySwap<int>(a,b);
cout<<"a=" << a<<";"<<"b="<<b<<endl;
double c=1.1;
double d=2.2;
mySwap(c,d);
cout<<"c=" << c<<";"<<"d="<<d<<endl;
return 0;
}

/**
函数模板注意事项:
1.自动类型推导,必须推导出一致的数据类型T才可以使用,就是这部分所有T对应一种类型
2.模板必须要确定出T的数据类型才能使用。 就是函数体要有T,如果没有,要使用显示指定类型


普通函数与函数模板的区别:
普通函数调用时,可以发生隐式类型转换。
int myAdd(int a,int b){
return a + b;
}
void test(){
int a = 10;
int b = 20;
char c = 'c'; //A-65 a-97
cout << myAdd(a,c)<< endl; //隐式类型转换,将字符型变量转换成了整型变量
}
函数模板调用时,如果可以利用自动类型推导,则不会发生隐式类型转换;若使用显示指定类型,可以发生隐式类型转换。
可以通过空模板参数列表来强制调用函数模板
函数模板也可以重载,并且如果普通函数和函数模板都可以调用,优先调用普通函数
1
void A(int a,int b){
cout<<"ab"<<endl;
}
2
template<class T>
void A(T a, T b){
cout<<"AB"<<endl;
}
3,是对2的重载
template<class T>
void A(T a, T b,T c){
cout<<"ABC"<<endl;
}

此时会调用1,但可以通过空模板参数列表来强制调用2
int main(){
int a=10,b=20;
A<>(a,b);//通过空模板参数列表来强制调用2
return 0;
}
**/

类模板

#include<iostream>
#include<string>
using namespace std;

//一、类模板
template<class T, class U>
class Person{
public:
Person(T name,U age){
this->name = name;
this->age = age;
}

T name;
U age;

void showPerson(){
cout << "name:" << this->name << "\n" << "age:" << this->age << endl;
}
};

int main(){
Person<string,int>p("张三",18);
p.showPerson();
return 0;
}


//二、类模板和函数模板的区别:
//1.类模板没有自动类型推导的使用方法 2.类模板在模板参数列表中可以有默认参数 template<class T, class U = int>


//三、类模板中成员函数创建时机:普通类中的成员函数一开始就可以创建,类模板中的成员函数在调用时才创建


//四、类模板对象做函数参数:
//一共有三种传入方式:1.指定传入的类型 2.参数模板化 3.整个类模板化
template<class T1,class T2>
class Person{
public:
Person(T1 name, T2 age){
this->name = name;
this->age = age;
}
void showPerson(){
cout << "name:" << this->name << "\n" << "age:" << this->age << endl;
}
T1 name;
T2 age;
};
//1.指定传入的类型
void printPerson1(Person<string,int> &p){
p.showPerson();
}
void test1(){
Person<string,int>p("lisi",19);
printPerson1(p);
}
//2.参数模板化
template<class T1,class T2>
void printPerson2(Person<T1,T2> &p){
p.showPerson();
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test2(){
Person<string,int>p("lisi",20);
printPerson2(p);
}
//3.整个类模板化,建议使用
template<class T>
void printPerson3(T &p){
p.showPerson();
}
void test3(){
Person<string,int>p("lisi",21);
printPerson3(p);
}


//五、类模板与继承:
template<class T>
class Base{
T m;
};
// class Son:public Base{}; 错误,需要指定Base的T类型
class Son1:public Base<int>{
};
//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1,class T2>
class Son2:public Base<T1>{
T2 obj;
};


//六、类模板成员函数类外实现
#include<iostream>
#include<string>
using namespace std;

template<class T3,class T4>
class Person1{
public:
Person1(T3 name,T4 age);
// {
// this->name=name;this->age=age;
// }
void showPerson();
// {
// }
T3 name;
T4 age;
};
//构造函数类外实现
template<class T3,class T4>
Person1<T3,T4>::Person1(T3 name,T4 age){
this->name=name;
this->age=age;
}
//成员函数类外实现
template<class T3,class T4>
void Person1<T3,T4>::showPerson(){
cout<< "sb" << endl;
}


// 七、类模板分文件编写
//问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
//解决方法:
// 1.直接包含.cpp源文件:
//一般把类及其方法的声明放在.h文件中,把实现放在.cpp文件中。
// 三个文件:person.h;person.cpp;main.cpp 在person.cpp中要写#include"person.h" 在main.cpp中要包含#include"person.cpp"
// 2.将声明和实现写到同一文件中,并更改后缀名为.hpp。hpp是业界约定,不是必须。 然后在main.cpp中包含#include"person.hpp"


//八、类模板与友元
//掌握类模板配合友元函数的类内和类外实现
//全局函数类内实现:直接在类内声明友元即可
/**
#include<iostream>
#include<string>
using namespace std;
//通过全局函数 打印Person信息
template<class T5 , class T6>
class Person2{
//全局函数 类内实现 friend关键字能访问private数据
friend void printPerson(Person2<T5,T6> p){
cout << "name:" << p.name << " age:" << p.age << endl;
}
public:
Person2(T5 name,T6 age){
this->name = name;
this->age = age;
}
private:
T5 name;
T6 age;
};
int main(){
Person2<string,int>p("aaa",18);
printPerson(p);
return 0;
}
**/
//全局函数类外实现:需要提前让编译器知道全局函数的存在(较难)

函数对象

#include<iostream>
#include<string>
#include<functional>
using namespace std;

/**
函数对象

概念:
重载操作符的类的对象叫函数对象,重载的是()时,又叫仿函数

一、特点:
1.函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
2.函数对象超出普通函数的概念,函数对象可以有自己的状态
3.函数对象可以作为参数传递
**/

//1.函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
class Myadd{
public:
int operator()(int v1,int v2){
return v1+v2;
}
};
void test01(){
Myadd myadd;
cout << myadd(10,20) << endl ;
}

//2.函数对象超出普通函数的概念,函数对象可以有自己的状态
class Myprint{
public:
Myprint(){
this->count = 0;
}
void operator()(string s){
cout << s << endl;
this->count++;
}

int count; //内部自己状态
};
void test02(){
Myprint myprint;
myprint("aaaa");
myprint("aaaa");
myprint("aaaa");
cout << "Myprint调用的次数是" << myprint.count << "次" << endl;
}

//3.函数对象可以作为参数传递
void doPrint(Myprint &mp,string s){
Myprint myprint;
myprint(s);
}



int main(){
//test01();
//test02();
Myprint mp2;
doPrint(mp2,"cnm");
return 0;
}


//二、谓词
/**
返回bool类型的仿函数称为谓词
如果operator接受一个参数,叫做一元谓词
如果operator接受两个参数,叫做二元谓词
**/


//三、内建函数对象
/**
stl内建了一些函数对象,能够拿来直接使用。这些仿函数所产生的对象,用法和一般函数完全相同。
使用内建函数对象需要包含#include<functional>
分类:算数,关系,逻辑仿函数

1.算数仿函数
template<class T> T plus<T>; //加 plus<int> p; cout << p(10,20) << endl;
template<class T> T minus<T>; //减
template<class T> T multiplies<T>; //乘
template<class T> T divides<T>; //除
template<class T> T modulus<T>; //取模
template<class T> T negate<T>; //取反 negate<int> n; cout << n(50) << endl;

2.关系仿函数
template<class T> bool equal_to<T>;
template<class T> bool not_equal_to<T>;
template<class T> bool greater<T>;
template<class T> bool greater_equal<T>;
template<class T> bool less<T>; // greater<int>() 等价于之前的自定义的 Mycompare greater<int>()要加(),代表对象的创建。
template<class T> bool less_equal<T>;

3.逻辑仿函数
template<class T> bool logical_and<T>; //与
template<class T> bool logical_or<T>; //或
template<class T> bool logical_not<T>; //非
**/

读、写文件

/**
* @file readbinaryfile.cpp
* @author your name (you@domain.com)
* @brief 读取二进制数据
* @version 0.1
* @date 2022-05-26
*
* @copyright Copyright (c) 2022
*
*/
#include<iostream>
#include<fstream>
using namespace std;

class Person{
public:
char name[64];
int age;
};

int main(){
ifstream ifs;
ifs.open("person.txt",ios::in | ios::binary);
if(!ifs.is_open()){
cout << "打开失败" << endl;
return 0;
}
Person p;//接住读出的数据
ifs.read((char*)&p,sizeof(p)); //函数原型:istream& read(char * buffer,int len);
cout << "姓名:" << p.name << "\n" << "年龄:" << p.age << endl;

ifs.close();
}
//需要包含头文件<fstream>
//文件类型有两种 1.文本文件:文件以文本的ASCII码形式存储在计算机中 2.二进制文件:文件以文本的二进制形式存储在计算机中,不能直接读懂
//操作文件的三大类 ofstream:写操作 ifstream:读操作 fstream:读写操作

/**
* @brief 写文件步骤——“包流开写闭”

#include <fstream> //包含头文件
ofstream ofs; //创建流对象
ofs.open("文件路径",打开方式); //打开文件
ofs << "写入的数据"; //写数据
ofs.close(); //关闭文件


文件打开方式:
ios::in //为读文件而打开文件
i0s::out //为写文件而打开文件
ios::ate //初始位置:文件尾
ios::app //追加方式写文件
ios::trunc //如果文件存在,则先删除再创建
ios::binary //二进制方式
ios::binary | ios::out //用二进制方式写文件
*/

#include<iostream>
#include<fstream>
using namespace std;
int main(){

ofstream ofs;

ofs.open("test01.txt",ios::out);

ofs << "name:kongjiangang" << endl;
ofs << "gender:male" << endl;

ofs.close();
}
/**
* @file writebinaryfile.cpp
* @author your name (you@domain.com)
* @brief 向二进制文件写数据
* @version 0.1
* @date 2022-05-26
*
* @copyright Copyright (c) 2022
*
*/

#include<iostream>
#include<fstream>
using namespace std;

class Person{
public:
char name[64];
int age;
};

//包流开操闭
int main(){
ofstream ofs;
ofs.open("person.txt",ios::out | ios::binary);

Person p = {"张三",18};
ofs.write((const char *)&p,sizeof(p));//函数原型: ostream& write(const char * buffer,int len);

ofs.close();
}

STL

stl general
//stl:标准模板库
//stl从广义上分为容器,算法,迭代器。 容器和算法通过迭代器无缝衔接

//stl六大组件:容器,算法,迭代器,仿函数,适配器(配接器),空间配置器
//1.容器:分为序列式容器(强调值的排序)和关联式容器(二叉树)。各种数据结构,如vector,list,deque,set,map等,用来存放数据
//2.算法:分为质变算法(拷贝,置换,删除)和非质变算法(查找,计数,遍历)。常用的函数,如sort,find,copy,for_each等
//3.迭代器:分为(输入(对数据只读),输出(对数据只写),前向,双向,随机访问)迭代器。能按序访问某个容器内所含元素又不暴露该容器内部表示方法。
//4.仿函数:行为类似函数,可作为算法的某种策略
//5.适配器:不做要求
//6.空间配置器:负责空间的配置与管理


//一、容器算法迭代器初识:vector存放内置数据类型
//容器:vector 算法:for_each 迭代器:vector<int>::iterator
#include<iostream>
#include<vector> //需要包含头文件
#include<algorithm>//同上

using namespace std;
void myPrint(int val){
cout << val << endl;
}

void test01(){
//创建容器
vector<int> v;

//向容器中插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);

//通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin(); //起始迭代器,指向容器中的第一个元素
vector<int>::iterator itEnd = v.end(); //结束迭代器,指向容器中的最后一个元素的下一个位置

//第一种遍历方式
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
//第二种遍历方法
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
//第三种遍历方式,利用for_each算法,回掉
for_each(v.begin(),v.end(),myPrint);

}


//二、vector存放自定义数据类型,同一

//三、容器中再嵌套一个容器
void test02(){
vector< vector<int> > v;
//创建小容器
vector<int> v1;
vector<int> v2;
vector<int> v3;
vector<int> v4;

//在小容器中插入数据
for(int i=0;i<4;i++){
v1.push_back(i+1);
v2.push_back(i+1);
v3.push_back(i+1);
v4.push_back(i+1);
}

//将小容器插入到大容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);

//通过大容器遍历
for(vector<vector<int> >::iterator iterator=v.begin();iterator!=v.end();iterator++){
//(*iterator) 是一维数组
for(vector<int>::iterator vit=(*iterator).begin();vit!=(*iterator).end();vit++)
cout<< (*vit) << endl;
}
}


int main(){
test01();
cout << "\n" << endl;
test02();
return 0;
}
vector
#include<iostream>
#include<vector>
using namespace std;
int main(){

return 0;
}
//vector,也称单端数组
//vector与普通数组区别:不同之处是数组是静态空间,而vector可以动态扩展
//vector容器的迭代器是支持随机访问的迭代器


//一、构造函数
/**
vector<T> v;
vector(v.begin(),v.end()); //将v[begin(),end()]区间中的元素赋值给vector,如 vector<int> v2(v1.begin(),v1.end());
vector(n,elem); //初始值为n个elem
vector(const vector &vec);
**/


//二、赋值
/**
vector& operator=(const vector &vec);
assign(begin,end); //v2.assign(v1.begin(),v.end());
assign(n,elem); //v3.assign(10,20);
**/


//三、容量和大小
/**
empty(); //判断容器是否为空
capicity(); //容器的容量,不是创建vector,插入10个元素容量就为10,可能为16
size(); //返回容器中元素数量
resize(int num); //重新指定容器长度,若容器变长,则以默认值填充新位置;若容器变短,则末尾超过容器长度的元素被删除。
resize(int num,elem); //重新指定容器长度,若容器变长,则以elem填充新位置;若容器变短,则末尾超过容器长度的元素被删除。
**/


//四、插入和删除
/**
push_back(elem);
pop_back(); //删除最后一个元素
insert(const_iterator pos,elem);
insert(const_iterator pos,int n,elem); //在pos处插入n个elem

erase(const_iterator pos); //删除迭代器指向的元素
erase(const_iterator begin,onst_iterator end); //删除begin到end间的所有元素
clear(); //删除容器内所有元素
**/


//五、数据存取
/**
at(int idx);
operator[]; // vector<int> v; cout << v[n] << endl;
front(); //返回容器内第一个元素
back(); //返回容器内最后一个元素
**/


//六、互换容器
/**
swap(vec); //将vec与本身的元素进行互换 v1.swap(v2);
v.resize(3);vector<int>(v).swap(v); //巧用swap收缩内存,vector<int>(v)是匿名对象,拷贝构造函数,同时匿名对象自动析构。
**/


//七、预留空间
//减少vector在动态扩展容量时的扩展次数
/**
reserve(int len); //容器预留len个元素长度,但预留位置不初始化,且元素不可访问。
**/
priority_queue
class Solution {
public:
// 小顶堆
class mycomparison {
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
return lhs.second > rhs.second;
}
};

vector<int> topKFrequent(vector<int>& nums, int k) {
map<int, int> mp;
for(int e: nums)
mp[e]++;

priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que; // priority_queue<Type, Container, Functional>

for(auto i = mp.begin(); i != mp.end(); i ++){
pri_que.push(*i);
if(pri_que.size() > k) pri_que.pop();// 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
}

// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒叙来输出到数组
vector<int> res(k);
for (int i = k - 1; i >= 0; i--) {
res[i] = pri_que.top().first;
pri_que.pop();
}
return res;
}
};
#define pii pair<int, int>
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size(), res = 0;
auto comp = [&](pii& l, pii& r){
return matrix[l.first][l.second] > matrix[r.first][r.second];}; // 小根堆
priority_queue<pii, vector<pii>, decltype(comp)> q(comp);
for(int i = 0; i < n; ++i) q.emplace(i, 0); // push fist colum i行0列
while(--k){
auto [i, j] = q.top();
q.pop();
if(j != n-1) q.emplace(i, j+1);
}
auto [i, j] = q.top();
return matrix[i][j];
}
};
list
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
int main(){
return 0;
}
/**
list(链表,stl中的链表是一个双向循环链表,每个结点都有data,prev,next域)常用接口


一、构造函数:
list<T> l;
list(begin,end); //拷贝构造函数,list<int> l2(l1.begin(),l1.end());
list(n,elem);
list(const list &l);


二、赋值与交换:
assign(begin,end); //list<int> l2; l2.assign(l1.begin(),l1.end());
assign(n,elem);
list& operator=(const list &l);
swap(l) //将l与本身的元素互换 l1.swap(l2);


三、数据存取:
push(elem); //往队尾加元素
pop(); //从队头移除一个元素
back(); //返回最后一个元素
front(); //返回第一个元素


四、大小操作:
size();
empty();
resize(num);
resize(num,elem);


五、插入和删除:
push_back(elem);
push_front(elem);

pop_back();
pop_front();

insert(pos,elem); //返回新数据的位置
insert(pos,n,elem); //无返回值
insert(pos,begin,end); //无返回值

erase(begin,end); //返回下一个数据的位置
erase(pos); //返回下一个数据的位置

remove(elem); //移除所有与elem匹配的元素

clear(); //移除容器中所有数据


六、数据存取
front();
back();


七、反转和排序
reverse();
sort();
**/
map
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
int main(){
return 0;
}
/**
map & multimap 容器

map基本概念:
1.map中的所有元素都是pair
2.pair中的第一个元素为key,起索引作用。第二个元素为value(实际值)
3.所有元素都会根据元素的键值自动排序
4.map & multimap区别:map不允许容器中有重复key值的元素,multimap允许

一、构造与赋值
map<T1,T2> mp;
map(const map &mp);
map& operator=(const map &mp); //返回值是一个map的引用变量


二、赋值与交换
list& operator=(const set &st);


三、大小与交换
size();
emtpy();
swap(mp); //mp1.swap(mp2);


四、插入和删除
insert(elem); //map<int,int> mp1; mp1.insert(pair<int,int>(1,20));
clear();
erase(pos); //返回下一个元素的迭代器
erase(begin,end); //返回下一个元素的迭代器
erase(key); //删除容器中值为elem的元素


五、查找与统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回mp.end()
count(key); //统计值为key的元素个数


六、排序
默认按key值从小到大排序,但可以通过仿函数改变
class MyCompare{
public:
bool operator()(int v1,int v2){ //第一个括号是要重载的符号,第二个是传入的参数
return v1>v2;
}
};
map<int,int,MyCompare> mp; //指定排序规则
**/
set
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
int main(){
return 0;
}
/**
set & multiset 容器

set基本概念:
1.所有元素都会在插入时自动被排序
2.set/multiset是关联式容器,底层用二叉树实现
3.set和multiset区别:set不允许容器中有相同元素,multiset允许


一、构造函数
set<T> st;
set(const set &st);


二、赋值与交换
list& operator=(const set &st);


三、大小与交换
size();
emtpy();
swap(st); //st1.swap(st2);


四、插入和删除
insert(elem);
clear();
erase(pos); //返回下一个元素的迭代器
erase(begin,end); //返回下一个元素的迭代器
erase(elem); //删除容器中值为elem的元素


五、查找与统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回st.end()
count(key); //统计值为key的元素个数
st.lower_bound(x); // 返回第一个>=x的迭代器
st.upper_bound(x); // 返回第一个>x的迭代器


六、set和multiset的区别
set插入数据时会返回是否插入成功,multiset不会


七、pair 对组的创建
pair<type,type> p(value1,value2); //p.first和p.second分别访问不同部分
pair<type,type> p = make_pair(value1,value2);


八、set容器排序
学习目标:set容器默认排序规则为从小到大,掌握如何改变排序规则
主要技术点:利用仿函数可以改变排序规则
1.set中存放内置数据类型
class MyCompare{
public:
bool operator()(int v1,int v2){ //第一个括号是要重载的符号,第二个是传入的参数
return v1>v2;
}
};
set<int,MyCompare> s2; //指定排序规则

2.set中存放自定义数据类型
class Person{
public:
Person(string name,int age){
this->name = name;
this->age = age;
}
string name;
int age;
};
class MyCompare2{
public:
bool operator()(const Person &p1,const Person &p2){ //第一个括号是要重载的符号,第二个是传入的参数
return p1.age>p2.age; //按年龄降序排列
}
};
int main(){
set<Person,MyCompare2> st;
Person p1("lisi",19);
Person p2("lisiguang",20);
st.insert(p1);
st.insert(p2);
//迭代器输出,省略……

return 0;
}

**/
stack
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
int main(){
return 0;
}
/**
stack常用接口

一、构造函数:
stack<T> s;
stack(const stack &s);

二、赋值操作:
stack& operator=(const stack &s);

三、数据存取:
push(elem);
pop(); //移除栈顶元素
top(); //返回栈顶元素
emplace(); //emplace可以直接传入构造对象需要的元素,然后自己调用其构造函数.相当于emplace直接把原料拿进家,造了一个。而push是造好了之后,再复制到自己家里,多了复制这一步。

四、大小操作:
empty();
size();
**/
queue
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
int main(){
return 0;
}
/**
queue常用接口

一、构造函数:
queue<T> q;
queue(const queue &q);

二、赋值操作:
queue& operator=(const queue &q);

三、数据存取:
push(elem); //往队尾加元素
pop(); //从队头移除一个元素
back(); //返回最后一个元素
front(); //返回第一个元素

四、大小操作:
empty();
size();
**/
deque
// 一、 构造函数
deque();// 创建一个空deque
deque(int nSize); // 创建一个deque,元素个数为nSize
deque(int nSize,const T& t);// 创建一个deque,元素个数为nSize,且值均为t
deque(const deque &); // 复制构造函数

// 二、 增加函数
void push_front(const T& x); // 双端队列头部增加一个元素X
void push_back(const T& x); // 双端队列尾部增加一个元素x
iterator insert(iterator it,const T& x); // 双端队列中某一元素前增加一个元素x
void insert(iterator it,int n,const T& x); // 双端队列中某一元素前增加n个相同的元素x
void insert(iterator it,const_iterator first,const_iterator last); // 双端队列中某一元素前插入另一个相同类型向量的[first,last)间的数据

// 三、删除函数
iterator erase(iterator it); // 删除双端队列中的某一个元素
iterator erase(iterator first,iterator last); // 删除双端队列中[first,last)中的元素
void pop_front(); // 删除双端队列中最前一个元素
void pop_back(); // 删除双端队列中最后一个元素
void clear(); // 清空双端队列中所有元素

// 四、遍历函数
reference at(int pos); // 返回pos位置元素的引用
reference front(); // 返回首元素的引用
reference back(); // 返回尾元素的引用
iterator begin(); // 返回向量头指针,指向第一个元素
iterator end(); // 返回指向向量中最后一个元素下一个元素的指针(不包含在向量中)
reverse_iterator rbegin(); // 反向迭代器,指向最后一个元素
reverse_iterator rend(); // 反向迭代器,指向第一个元素的前一个元素

// 五、判断函数
bool empty() const; // 向量是否为空,若true,则向量中无元素

// 六、大小函数
int size() const; // 返回向量中元素的个数
int max_size() const; // 返回最大可允许的双端对了元素数量值

// 七、其他函数
void swap(deque&); // 交换两个同类型向量的数据
void assign(int n,const T& x); // 向量中第n个元素的值设置为x
string
//char*是一个指针;string是一个类,类内部封装了char*,是一个char*型的容器
//string类内部封装了很多成员方法,如find,copy,delete,replace,insert。 string类管理char*所分配的内存,不用担心复制越界和取值越界,由类内部负责管理

//一、构造函数
/**
string(); //创建一个空的字符串,例如string str;
string(const char* s); //使用字符串s初始化,如 char* str = "hello world"; string s(str);
string(const string& str); //使用一个string对象初始化另一个string对象
string(int n,char c); //使用n个字符c初始化
**/

#include<iostream>
#include<string>
using namespace std;
int main(){

return 0;
}

//二、赋值
/**
string& operator=(const char* s); //string str = "aaa";
string& operator=(const string& s); //string str1 =str;
string& operator=(const char c); //strng str = 'a';
string& assign(const char* s); //string str2; str2.assign("aaaaa");
string& assign(const char* s,int n); //把字符串s的前n个字符赋值给当前字符串
string& assign(const string &s); //把字符串s赋值给当前字符串
string& assign(int n,char c); //用n个字符c赋值给当前字符串
**/


//三、拼接
/**
string& operator+=(const char* str); //string str="我"; str+="爱玩游戏";
string& operator+=(const char c); //string str="我"; str+='艹';
string& operator+=(const &string str); //str1 += str2;
string& append(const char* s); //string str="我"; str.append("爱玩游戏");
string& append(const char* s,int n); //把字符串s的前n个字符连接到当前字符串结尾
string& append(const string &s); //str1.append(str2);
string& append(const string &s,int pos,int n); //把字符串s从pos开始n个字符连接到当前字符串结尾
**/


//四、查找和替换
/**
int find(const string& str,int pos=0) const; //查找str第一次出现的位置,从pos开始寻找。不写pos默认为0
int find(const char* s,int pos=0) const; //查找s第一次出现的位置,从pos开始寻找
int find(const char* s,int pos,int n) const; //查找s的前n个字符第一次出现的位置,从pos开始寻找
int find(const char c ,int pos=0) const; //查找字符c第一次出现的位置,从pos开始寻找

int rfind(const string& str,int pos=npos) const; //查找str最后一次出现的位置,从pos开始寻找
int rfind(const char* s,int pos=npos) const; //查找s最后一次出现的位置,从pos开始寻找
int rfind(const char* s,int pos,int n) const; //查找s的前n个字符最后一次出现的位置,从pos开始寻找
int rfind(const char c ,int pos=0) const; //查找字符c最后一次出现的位置,从pos开始寻找

string& replace(int pos,int n,const string& str); //替换从pos开始的n个字符为字符串str
string& replace(int pos,int n,const char* s); //替换从pos开始的n个字符为字符串s
**/


//五、比较
/**
字符串是按字符的ASCII进行比较 =返回0;>返回1;<返回-1
int compare(const string& s) const;
int compare(const char* s) const;
**/


//六、获取(也可以赋值,修改)
/**
char& operator[](int n); //通过[]方式获取字符 cout << str[i] << endl;
char& at(int n); //通过at获取字符 cout << str.at(i) << endl;
**/


//七、插入和删除
/**
string& insert(int pos,const char* s); //在pos处插入s
string& insert(int pos,const string& s); //在pos处插入s
string& insert(int pos,int n,char c); //在指定pos插入n个c

string& erase(int pos,int n=npos); //删除从pos开始的n个字符 ,nopos表示不存在的位置
**/


//八、获取子串
/**
string substr(int pos = 0;int n = npos) const; //返回由pos开始n个字符组成的子串
//如string str="abcdef"; cout << str.substr(1,3) << endl;返回"bcd"
**/

刷题输入输出

// 法1 最基本,也是最常用的用法
int a,b;
cin>>a>>b;
cout<<a+b<<endl;// 输入:2[回车]3[回车] 输出:5
// 法2 接受一个字符串,遇	“ ”、[TAB]、[回车]都结束
char a[20];
cin>>a;
cout<<a<<endl; // 输入:jkljkl jkljkl 输出:jkljkl(遇空格结束)
// cin.get()可以用来接收单个字符
char ch;
cin.get(ch);
cout<<ch<<endl; // 输入:jljkljkl 输出:j

// getchar() 接受一个字符,需包含“#include<string>”
char ch;
ch=getchar();
cout<<ch<<endl;
// cin.getline() 接受一个字符串,可以接收空格并输出
char m[20];
cin.getline(m,5);
cout<<m<<endl; // 输入:jkljkljkl 输出:jklj(\0)

// getline() 接受一个字符串,可以接收空格并输出,需包含“#include<string>”
string st ;
cout<<"Input st:";
getline(cin,st);
cout<<st<<endl;
// gets() 接受一个字符串,可以接收空格并输出,需包含“#include<string>”
char m[20];
gets(m); //不能写成m=gets();
cout<<m<<endl;

刷题常用函数

to_string(int x)
class Solution {
public:
bool isPalindrome(int x) {
string s = to_string(x);
string s1 = s;
reverse(s.begin(), s.end());
return s == s1;
}
};
stoi(string s)

string 类型转为 int 类型

reverse(iterator begin, iterator end)
class Solution {
public:
bool isPalindrome(int x) {
string s = to_string(x);
string s1 = s;
reverse(s.begin(), s.end());
return s == s1;
}
};
islower/ isupper/isalpha/isdigit
isalnum(char ch)
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) { // 判断字符变量c是否为字母或数字,若是则返回非零,否则返回零。
sgood += tolower(ch);
}
}
string sgood_rev(sgood.rbegin(), sgood.rend());
return sgood == sgood_rev;
}
};
tolower(char ch)/toupper(char ch) /tolower(string s)/toupper(string s)
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) { // 判断字符变量c是否为字母或数字,若是则返回非零,否则返回零。
sgood += tolower(ch);
}
}
string sgood_rev(sgood.rbegin(), sgood.rend());
return sgood == sgood_rev;
}
};
next_permutation(iterator begin, iterator end)

函数将按字母表顺序生成给定序列的下一个较大的排列,直到整个序列为降序为止。

prev_permutation(iterator begin, iterator end)

函数与之相反,是生成给定序列的上一个较小的排列。

long int stol(const string&str)
class Solution {
public:
int nextGreaterElement(int n) {
auto tmp = to_string(n);
return next_permutation(tmp.begin(), tmp.end()) && stol(tmp) <= INT_MAX ? stol(tmp) : -1;
}
};

此函数将在函数调用中作为参数提供的字符串转换为long int。

sort && stable_sort

sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使用stable_sort函数,这里不过多介绍

由于在排序过程中涉及到元素交换等操作,所以sort函数仅支持可随机访问的容器,如数组, string, vector, deque等

方式一(默认) void sort (RandomAccessIterator first, RandomAccessIterator last);

sort(arr.begin(), arr.end()); // sort函数如果不传入第三个参数,则默认是升序排列

方式二(自定义) void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

实现降序排列,需传入第三个参数–比较函数,greater<type>(),这里的元素为int 类型,即函数为 greater<int>();

sort(arr.begin(), arr.end(), greater<int>()); 
lower_bound/ upper_bound
//在 [first, last) 区域内查找第一个 >=val 的元素的迭代器
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
const T& val);
//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
const T& val, Compare comp);
//查找[first, last)区域中第一个	>val 的元素的迭代器
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
const T& val, Compare comp);
replace

请实现一个函数,把字符串 s 中的每个空格替换成”%20”。

class Solution {
public:
string replaceSpace(string s) {
for(int i = 0; i < s.size(); i ++)
if(s[i]==' ') s.replace(i, 1, "%20");
return s;
}
};

C++ Primer Plus

  1. cout.put('A');cout << 'A';区别 : 前者打印A的ASCII码, 后者打印A

  2. const Month = 12 这样便可以在程序中使用Month而不是12了

  3. 强制转换的语法有两种: (typeName) valuetypeName (value) C++11新增:static_cast<typeName> value

  4. 在初始化声明中,如果使用auto,而不指定变量的类型,编译器将把变量的类型设置成 与初始值相同

  5. 数组:

    1. 声明 typeName arrayName[arraySize]; 其中arraySize不能为变量。
    2. 初始化时可以 int a[2]={1,2} 也可以 int a[3] ={1,2} 【长度为3,第三个元素默认为0】同样可以int a[] = {1,2} 【长度为2】
    3. C++11初始化数组时可以省略 = ;其次 int a = {}代表将a初始化为全0;最后 列表初始化禁止缩窄转换 如 不允许long a[] = {3, 1.2,1.3}
  6. 字符串string本质上是以'\0'结尾的char数组

    1. char a = 'A' 正确 char a = "A" 错误 因为” “是字符串 而’ ‘是字符
    2. 每次读取一行字符串输入: cin.getline(name, 20) // 使用getline( )将姓名读入到一个包含20个元素的name数组中
  7. 共用体:

    // 共用体(union)是一种数据格式,它能够存储不同的数据类型, 但只能同时存储其中的一种类型。
    union a{
    int int_val;
    long long_val;
    double double_val;
    };
    // 使用
    a pail;
    pail.int_val = 5; // valid
    pail.double_val = 5.20 // valid, int value is lost
  8. 结构体 enum color={red, green, blue} red等叫枚举量,分别对应0-2 也可以这样初始化enum a ={first, second = 100, third}这里,first在默认情况下为0。后面没有被初始化的枚举量的值将比 其前面的枚举量大1。因此,third的值为101。 也可以这样创建:enrm a ={aa, aaa = 0, bb, bbb = 1} 其中,aa和aaa都为0,bb和bbb都为1。

  9. 指针的写法可以: int* ptrint *ptrint*ptr

    // 1.
    int* ptr;
    ptr = 0xB8000000; // 报错
    ptr = (int *) 0xB8000000; //正确


    // 2.
    int *ptr = new int; //正确
    delete ptr; //正确,注意释放内存

    int a = 5;
    int *ptr = &a;
    delete ptr; // 错误,没有new 则不能delete


    // 3.
    // 动态数组和普通指针区别
    char *p1 = new char;
    char *p2 = new char[1000];
    *(p1 + 4) = 12; // 等价于 p1[4] = 12;
    delete p1;
    delete[] p2;


    // 4.
    int * ar = new arr[10];
    // 则
    arr[i] = *(ar + i);
    &arr[i] = ar + i;


    // 5.
    结构体对象访问成员用. 结构体指针访问成员用->
  10. 原型描述了函数到编译器的接口,也就是说,它将函数返回值的类 型(如果有的话)以及参数的类型和数量告诉编译器。

    1. 原型语法: void funcA(int, char);

    2. 函数原型的作用:

      1.帮助编译器正确处理函数返回值;

      2.帮助 编译器检查使用的参数数目是否正确;

      3.帮助编译器检查使用的参数类型是否正确。如果不正确,则转换为正确 的类型(如果可能的话)

  11. const保护数组: 使用普通参数时,这种保护将 自动实现,这是由于C++按值传递数据,而且函数使用数据的副本。然而,接受数组名的函数将使用原始数据,为防止函数无意中修改数组的内容,可在声明形参时 使用关键字const

    1. void noChangeArr(const int arr[], int n);
    2. 原型里也要加const
  12. 指针和const:

    int a = 10;
    const int * var1 = &a; //指向整形常量的指针,它指向的值不能修改
    *var1 = 20; //错
    var = &b; // 对

    int a = 10;
    int * const var2; //指向整形的常量指针,它不能在指向别的变量,但指向(变量)的值可以修改。
    *var2 = 20; // 对
    var2 = b; // 错

    const int* const var3; //指向整形常量 的常量指针。它既不能再指向别的常量,指向的值也不能修改。

    // 并且 C++禁止将const的地址赋给非const指针。
  13. 当且仅当声明函数的形参时,下面两个声明才是等价的 typeName arr[]typeName *arr

  14. 假设要将字符串作为参数传递给函数,则表示字符串的方式有3种:

    1. char数组;
    2. 被设置为字符串的地址的char指针。
    3. 用引号括起的字符串常量(也称字符串字面值)

​ 上述3种选择的类型都是char指针(准确的说是char*),因此可以将其作为字符串处理函数的参数。

char ghost[15] = "galloping";
char * str = "galloping";
int n1 = strlen(ghost); // ghost 是 &ghost[0] 对应1
int n2 = strlen(str); //str是 char 类型的指针 对应2
int n3 = strlen("galloping"); //"galloping" 是 address of string 对应3
  1. 函数指针:
    1. 获取函数的地址:只要使用函数名(后面不跟参数)即可。如:think()是一个函数,则think就是该函数的地址。
    2. 如果要将函数作为参数进行传递,必须传递函数名。
    3. 函数括号中的形参可有可无,视情况而定。
    4. 与指针函数区别开来,指针函数是返回值类型为指针的函数
函数指针的声明方法为:
返回值类型 ( * 指针变量名) ([形参列表]);

int func(int x); /* 声明一个函数 */
int (*f) (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */
f = &func; // 这也行,等同于上行的写法
typedef对函数指针进行简化:
typedef double real;//make real another name for double

typedef char(*pFun)(int) //也可写成:char(*pFun)(int);
/*typedef的功能是定义新的类型。第一句就是定义了一种PFUN的类型,
并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。*/

char glFun(int a){return;}
//定义了一个函数glFun().该函数正好是一个以int为参数返回char的函数。

void main(){
pFun =glFun; //对指针进行赋值。
(*pFun)(2); //通过指针调用函数glFun。
}
  1. 内联函数:编译器将使用相应的函数代码替换函数调用。[文本替换,会带来内存的占用]

使用内联函数的措施:

  • 在函数声明前加上关键字inline;
  • 在函数定义前加上关键字inline。
  1. 引用变量:
// 创建引用变量
int rats;
int & rodents = rats;
//上述引用声明允许将rats和rodents互换——它们指向相同的值和内存单元。
// 引用必须在声明时将其初始化。
// 引用更接近const指针,一旦与某个变量关联,就将一直效忠于它。
int * const pr = &rats;

类和对象

Example1
#include <iostream>
#include <string>
using namespace std;

const double PI = 3.14;

// 圆类
class Circle {
public: // 公共权限
int radius;
double calulateZC() { return 2 * radius * PI; }
};

// 学生类
class Student {
public:
// 属性
string sname;
string sid;

// 获取SID
void setID(string id) { sid = id; }
// 获取SName
void setName(string name) { sname = name; }
//打印学生信息
void printInfo() { cout << "学号:" << sid << "\t姓名:" << sname << endl; }
};


int main() {
// 实例化圆类
Circle c1;
// 给实例属性赋值
c1.radius = 3;
// 调用实例类中封装的行为--计算圆周长
cout << "半径为" << c1.radius << "的圆,周长为" << c1.calulateZC() << endl;

// 实例化学生类
Student stu1;
// 学生类属性赋值
stu1.sid = "10086";
stu1.sname = "移动";
// 调用实例用有的方法
stu1.printInfo();

Student stu2;
stu2.setID("110");
stu2.setName("刑警队");
stu2.printInfo();
return 0;
}
正方体
#include <iostream>
#include <string>
using namespace std;

class Cube {
private:
int length = 0;
int width = 0;
int height = 0;

public:
void set_LWH(int l, int w, int h) { // 设置长宽高
length = l;
width = w;
height = h;
}

int caculateArea() { // 计算面积
if ((length == 0) || (width == 0) || (height == 0)) {
cout << "\aYou must set the length, width and height of cube by function: set_LWH ." << endl;
return 0;
}
else return 2*(length * width + width * height + length * height);
}

int caculateVolume() { // 计算体积
if ((length == 0) || (width == 0) || (height == 0)) {
cout << "\aYou must set the length, width and height of cube by function: set_LWH ." << endl;
return 0;
}
else return length * width * height;
}

void isSame(Cube &c) {
if (caculateVolume() == c.caculateVolume()) cout << "两个立方体体积相等!" << endl;
else cout << "两个立方体体积不相等!" << endl;
}
};

void isEqual(Cube &c1, Cube &c2) {
if (c1.caculateVolume() == c2.caculateVolume()) cout << "两个立方体体积相等!" << endl;
else cout << "两个立方体体积不相等!" << endl;
}

int main() {
Cube cube1;
cube1.set_LWH(2, 3, 4);
cout << "面积为:" << cube1.caculateArea() << endl;
cout << "体积为:" << cube1.caculateVolume() << endl;

Cube cube2;
cube2.set_LWH(2, 4, 3);
cout << "面积为:" << cube2.caculateArea() << endl;
cout << "体积为:" << cube2.caculateVolume() << endl;

// 判断cube1与cube2体积是否相等
// 全局方法
isEqual(cube1, cube2);
// 成员方法
cube2.isSame(cube1);
return 0;
}
析构
#include <iostream>
#include <string>
using namespace std;

class Person {
public:
int age = 0;

Person() { cout << "无参构造函数!" << endl; } // 无参构造

Person(int a) { age = a; cout << "有参构造函数!" << endl; } // 有参构造

Person(const Person& p) { // 拷贝构造 必须加const
age = p.age;
cout << "这是拷贝构造函数!\t" << age << endl;
}
~Person() { cout << "这是析构函数!" << endl; }
};


// 2.调用
// 2.1 括号法
void test01() {
Person p1; // 无参
Person p2(10); // 有参
Person p3(p2); // 拷贝

}

// 2.2显示法
void test02() {
Person p1; // 无参
Person p2 = Person(10); // 有参
Person p3 = Person(p2); // 拷贝
}

// 2.3隐式转换法
void test03() {
Person p1; // 无参
Person p2 = 10; // 有参
Person p3 = p2; // 拷贝
}
int main() {
// test01();
//test02();
test03();
return 0;
}
常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可修改
#include <iostream>
using namespace std;

class Person {
public:
void showCls() const { // ==> const Pesron * const this
// this->m_age = 100; // 不可修改指针指向的值
// this = NULL; // 也不可修改地址指向
this->m_height = 100.0; // 可修改
}

void showAge() {}

int m_age;
mutable float m_height; // 特殊变量,即在常函数中也可修改这个值,加mutable关键字
};

int main() {
Person p;
p.showCls();

const Person p1;
// p1.m_age = 20; 不可修改
p1.m_height = 20.0; // 可修改,因为类定义里该变量前加了mutable

// 常对象只能调用常函数
p1.showCls();
// p1.showAge(); // 常对象不可调用普通成员函数
return 0;
}
运算符重载
#include <iostream>
using namespace std;

class Person {
};

// 只能利用全局函数重载左移运算符
ostream& operator<<(ostream &out, Person &p) {
out << "class Person";
return out;
}
/*
int main() {
Person p;
cout << p << endl;
return 0;
}*/
#include <iostream>
using namespace std;

class Myinteger {
friend ostream& operator<<(ostream& cout, Myinteger myint);
int m_Num;
public:
Myinteger() { m_Num = 0; }

// 前置递增运算符++重载 返回引用是为了对一个数进行递增
Myinteger& operator++() {
++m_Num;
return *this;
}

// 后置递增运算符++重载
Myinteger operator++(int) {
Myinteger temp = *this; // 记录当前的值

m_Num++; // 加操作

return temp; // 返回记录值
}

// 前置递减运算符--重载
Myinteger& operator--() {
--m_Num;
return *this;
}

// 后置递减运算符--重载
Myinteger operator--(int) {
// 先记录原始值
Myinteger temp = *this;

// 减操作
m_Num--;

// 返回记录值
return temp;
}

};

// 重载<<运算符
ostream& operator<<(ostream& cout, Myinteger myint) {
cout << myint.m_Num;
return cout;
}

int main() {
Myinteger myint;

cout << myint << endl;
cout << ++myint << endl;

cout << myint++ << endl;
cout << myint << endl;

cout << --myint << endl;
cout << myint << endl;

cout << myint-- << endl;
cout << myint << endl;
}
= 重载
#include <iostream>
using namespace std;

class Person {
public:
Person(int age) {
m_age = new int(age);
}
~Person() {
if (m_age != NULL) {
delete m_age;
m_age = NULL;
}
}

// 重载 赋值运算符
Person& operator=(Person& p) {
// 编译器是提供浅拷贝
// m_age = p.m_age;

// 应该先判断是否有属性在堆区,如果有先释放,然后再深拷贝
if (this->m_age != NULL) {
delete m_age;
m_age = NULL;
}
// 深拷贝
m_age = new int(*p.m_age);
return *this;
}
int *m_age;
};

int main() {
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1; // 赋值操作
cout << *p1.m_age << endl;
cout << *p2.m_age << endl;
cout << *p3.m_age << endl;

return 0;
}
仿函数
#include <iostream>
#include <string>
using namespace std;

class MyPrint {
public:
void operator() (string test) {
cout << test << endl;
}
};

int main() {
MyPrint myprint;
myprint("hello world."); // 由于使用起来很像函数调用,因此也称为仿函数
return 0;
}
继承
#include <iostream>
using namespace std;

class Person {
public:
// Person() { cout << "Person类" << endl; }
void showGrade(){cout << "年级:" << m_grade << "年级" << endl;}
private:
int m_grade = 3;
};

class Man:public Person {
public:
void showSex() { cout << "男生" << endl; }
};

class Woman :public Person {
public:
void showSex() { cout << "女生" << endl; }
};

int main() {
Man m;
m.showGrade();
m.showSex();

Woman w;
w.showGrade();
w.showSex();

return 0;
}
#include <iostream>
using namespace std;

// 父类
class Base {
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};

// 公共继承
class Son1:public Base {
void func() {
m_a = 10; // 父类中的公共权限成员到子类中依然是公共权限
m_b = 10; // 父类中的保护权限成员到子类中依然是保护成员
// m_c = 10; // 父类中的保护权限成员 子类访问不到
}
};

// 保护继承
class Son2 :protected Base {
void func() {
m_a = 10; // 父类中的公共权限成员到子类中是保护权限
m_b = 10; // 父类中的保护权限成员到子类中依然是保护权限
// m_c = 10; // 父类中的私有成员访问不到
}
};

// 私有继承
class Son3:private Base {
void func() {
m_a = 100; // 父类中的公共成员到子类中是私有权限
m_b = 100; // 父类中的保护权限成员到子类中是私有权限
// m_c = 100; // 父类中的私有成员访问不到
}
};

class grandson3:public Son3 {
void func() {
// m_a; // m_a父类中已经通过私有继承将父父类中的公共权限变成私有权限,所以子类无法再继承该变量,m_b同理
}
};
int main() {
// 公共继承
Son1 s1;
s1.m_a = 100;
// s1.m_b = 100; // 父类中的保护权限成员到子类中依然是保护成员, 在类外访问不到

// 保护继承
Son2 s2;
// s2.m_a = 100; // 保护继承方式下父类的公共权限到子类中就是保护权限,类外访问不到

// 私有继承
Son3 s3;
// s3.m_a; // 私有继承方式下父类的公共权限到子类中就是私有权限,类外访问不到
// s3.m_b; // 私有继承方式下父类的保护权限到子类中就是私有权限,类外访问不到
return 0;
}
计算器
#include <iostream>
using namespace std;

// 普通写法:计算器类
class Caculator1 {
private:
int num1;
int num2;
string flag;
private:
int add() { return num1 + num2; }
int minus() { return num1 - num2; }
int times() { return num1 * num2; }
int divided() { return num1 / num2; }
public:
Caculator1(int n1, string f, int n2) :num1(n1), flag(f), num2(n2) {}
void show() {
if (flag == "+") cout << num1 << flag << num2 << "=" << add() << endl;
if (flag == "-") cout << num1 << flag << num2 << "=" << minus() << endl;
if (flag == "*") cout << num1 << flag << num2 << "=" << times() << endl;
if (flag == "/") cout << num1 << flag << num2 << "=" << divided() << endl;
}
};

// 利用多态实现计算器类
/* 多态带来的好处:
1. 组织结构清晰
2. 可读性强
3. 对于前期和后期的维护性高
*/
// 实现计算器抽象类
class AbstractCaculator {
public:
virtual int getResult() { return 0; }
int m_Num1;
int m_Num2;
};

// 加法计算器类
class AddCaculator :public AbstractCaculator {
public:
int getResult() { return m_Num1 + m_Num2; }
};

// 减法计算器类
class MinusCaculator :public AbstractCaculator {
public:
int getResult() { return m_Num1 - m_Num2; }
};

// 乘法计算器类
class MultiplyCaculator :public AbstractCaculator {
public:
int getResult() { return m_Num1 * m_Num2; }
};

// 普通写法测试
void test01() {
int num1, num2;
string flag;
cin >> num1 >> flag >> num2;
Caculator1 c(num1, flag, num2);
c.show();
}

// 多态写法测试
void test02() {
// 多态使用条件
// 父类引用指向子类对象
AbstractCaculator* abc = new AddCaculator;
abc->m_Num1 = 10;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc; // 只是销毁new对象在堆区的数据

abc = new MinusCaculator;
abc->m_Num1 = 10;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;

abc = new MultiplyCaculator;
abc->m_Num1 = 10;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main() {
// test01();
test02();
return 0;
}
电脑
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

// 抽象cpu基类
class AbsCPU {
public:
virtual void caculate() = 0;
};
// 抽象显卡基类
class AbsVideoCard {
public:
virtual void show() = 0;
};
// 抽象内存条基类
class AbsMemory {
public:
virtual void storage() = 0;
}

// intel厂商类
class IntelCPU :public AbsCPU {
public:
void caculate() { cout << "Intel CPU is caculating." << endl; }
};
class IntelVideoCard :public AbsVideoCard {
public:
void show() { cout << "Intel video card is working." << endl; }
};
class IntelMemory :public AbsMemory {
public:
void storage() { cout << "Intel memory is to storage." << endl; }
};

// Lenovo厂商类
class LenovoCPU :public AbsCPU {
public:
void caculate() { cout << "Lenovo CPU is caculating." << endl; }
};
class LenovoVideoCard :public AbsVideoCard {
public:
void show() { cout << "Lenovo video card is working." << endl; }
};
class LenovoMemory :public AbsMemory {
public:
void storage() { cout << "Lenovo memory is to storage." << endl; }
};

// 电脑类
class Computer {
public:
Computer(string* desc, AbsCPU *cpu, AbsVideoCard *vc, AbsMemory *mem) :m_desc(desc), m_cpu(cpu), m_vc(vc), m_mem(mem){}
~Computer() {
if (m_cpu != NULL) { delete m_cpu; m_cpu = NULL; }
if (m_vc != NULL) { delete m_vc; m_cpu = NULL; }
if (m_mem != NULL) { delete m_mem; m_cpu = NULL; }
if (m_desc != NULL) { delete m_desc; m_desc = NULL; }
}
void makeup() {
cout << setw(25) << setfill('*') <<"开始组装" << *m_desc << setw(15) << "" << endl;
m_cpu->caculate();
m_vc->show();
m_mem->storage();
cout << setw(30) << setfill('*') << "组装完成" << setw(20) << "" << endl;
}
private:
string* m_desc;
AbsCPU* m_cpu;
AbsVideoCard* m_vc;
AbsMemory* m_mem;
};

void test003() {
// 第一台电脑零件
AbsCPU* intelCPU = new IntelCPU;
AbsVideoCard* intelVideoCard = new IntelVideoCard;
AbsMemory* intelMemory = new IntelMemory;

// 创建第一台电脑
string* computer_name1 = new string("Intel电脑");
Computer* computer1 = new Computer(computer_name1, intelCPU, intelVideoCard, intelMemory);
computer1->makeup();
delete computer1;

// 第二台电脑零件
AbsCPU* lenovoCPU = new LenovoCPU;
AbsVideoCard* lenovoVideoCard = new LenovoVideoCard;
AbsMemory* lenovoMemory = new LenovoMemory;

// 创建第二台电脑
string* computer_name2 = new string("Lenovo电脑");
Computer* computer2 = new Computer(computer_name2, lenovoCPU, lenovoVideoCard, lenovoMemory);
computer2->makeup();
delete computer2;

// 创建第三台电脑
string* computer_name3 = new string("混搭电脑 ");
// 直接在传参过程中new对象
Computer* computer3 = new Computer(computer_name3, new IntelCPU, nw LenovoVideoCard, new LenovoMemory);
computer3->makeup();
delete computer3;
}

int main() {
test003();
return 0;
}

读写文件

#include <iostream>
// 1.添加头文件
#include <fstream>
using namespace std;

int main() {
// 2.创建流对象
fstream ofs;
// 3.指定打开方式
ofs.open("test.txt", ios::out);
// 4.写入内容,endl后会换行
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:20" << endl;
// 5.关闭文件
ofs.close();
return 0;
}

// 案例
#include <iostream>
#include <fstream>
using namespace std;

int main() {
fstream ofs;
ofs.open("水仙花数.txt", ios::out);
int num = 100;
while (num < 1000) {
int hunderd = num / 100;
int ten = num % 100 / 10;
int n = num % 10;
if (pow(hunderd, 3) + pow(ten, 3) + pow(n, 3) == num) {
ofs << num << "\t";
}
num++;
}
ofs.close();
return 0;
}

#include <iostream>
#include <fstream>
using namespace std;

int main() {
fstream ifs;
ifs.open("test.txt", ios::in);
char buffer[1024];
// 读取方式1
//while (!ifs.eof())
//{
// ifs.getline(buffer, 1024);
// cout << buffer << endl;
//}
// 读取方式2
//while (ifs >> buffer)
//{
// cout << buffer << endl;
//}
//ifs.close();
// 读取方式3
//while (ifs.getline(buffer, sizeof(buffer)))
//{
// cout << buffer << endl;
//}
// 读取方式4:一次只读一个字符
char c;
while ((c = ifs.get()) != EOF) { // EOF : end of file
cout << c;
}
return 0;
}

二进制方式读写文件

#include <iostream>
#include <fstream>
using namespace std;

class Person {
public:
char m_Name[64];
int m_Age;
};

void test01() {
fstream ofs("person.txt", ios::out | ios::binary);
// 写入
Person p = { "张三", 18 };
ofs.write((const char *)&p, sizeof(Person));
ofs.close();
}

int main() {
test01();
return 0;
}

#include <iostream>
#include <fstream>
using namespace std;

class Person{
public:
char m_Name[64];
int m_Age;
};

int main() {
// 打开文件
fstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open()) { cout << "打开文件失败" << endl; return 0; }
// 读文件
Person p;
ifs.read((char*)&p, sizeof(Person));
// 打印内容
cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
ifs.clear();
return 0;
}

多进程

1. 并行计算 Pi 的值

以下代码展示了如何使用多进程并行计算 Pi 的值。该程序将计算过程分为多个进程并行执行,最后将结果合并得到 Pi 的值。

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <unistd.h>
#include <sys/wait.h>

using namespace std;

const int NUM_PROCESSES = 4; // 进程数量
const int NUM_ITERATIONS = 1000000000; // 迭代次数

double calculate_pi(int start, int end) {
double sum = 0;
for (int i = start; i < end; i++) {
double x = (i + 0.5) / NUM_ITERATIONS;
sum += 4.0 / (1.0 + x * x);
}
return sum;
}

int main() {
pid_t pid;
double pi = 0;
for (int i = 0; i < NUM_PROCESSES; i++) {
pid = fork();
if (pid == 0) {
double result = calculate_pi(i * NUM_ITERATIONS / NUM_PROCESSES, (i + 1) * NUM_ITERATIONS / NUM_PROCESSES);
exit(result);
}
else if (pid < 0) {
cerr << "Error: fork failed." << endl;
exit(1);
}
}

for (int i = 0; i < NUM_PROCESSES; i++) {
int status;
pid_t child_pid = wait(&status);
if (WIFEXITED(status)) pi += WEXITSTATUS(status);
else {
cerr << "Error: child process " << child_pid << " failed." << endl;
exit(1);
}
}

cout << "Pi is approximately " << pi / NUM_ITERATIONS << endl;
return 0;
}
2.生产者消费者模型
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>

using namespace std;

const int BUFFER_SIZE = 10; // 缓冲区大小
const int NUM_ITEMS = 100; // 生产的数据数量

struct buffer {
int count;
int data[BUFFER_SIZE];
};

void produce_item(int item) {
cout << "Producing item: " << item << endl;
}

void consume_item(int item) {
cout << "Consuming item: " << item << endl;
}

void producer(buffer* buf) {
for (int i = 0; i < NUM_ITEMS; i++) {
while (buf->count == BUFFER_SIZE) {
sleep(1);
}
produce_item(i);
buf->data[buf->count++] = i;
}
}

void consumer(buffer* buf) {
for (int i = 0; i < NUM_ITEMS; i++) {
while (buf->count == 0) {
sleep(1);
}
int item = buf->data[--buf->count];
consume_item(item);
}
}

int main() {
buffer* buf = (buffer*)mmap(NULL, sizeof(buffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) {
cerr << "Error: mmap failed." << endl;
exit(1);
}
buf->count = 0;

pid_t pid = fork();
if (pid == 0) {
consumer(buf);
exit(0);
}
else if (pid < 0) {
cerr << "Error: fork failed." << endl;
exit(1);
}

producer(buf);

int status;
pid_t child_pid = wait(&status);
if (WIFEXITED(status)) {
cout << "Child process " << child_pid << " exited with status " << WEXITSTATUS(status) << endl;
}
else {
cerr << "Error: child process " << child_pid << " failed." << endl;
exit(1);
}

munmap(buf, sizeof(buffer));

return 0;
}

多线程

1.并行计算Pi的值
#include <iostream>
#include <cmath>
#include <thread>
#include <vector>

using namespace std;

const int NUM_THREADS = 4; // 线程数量
const int NUM_ITERATIONS = 1000000000; // 迭代次数

double calculate_pi(int start, int end) {
double sum = 0;
for (int i = start; i < end; i++) {
double x = (i + 0.5) / NUM_ITERATIONS;
sum += 4.0 / (1.0 + x * x);
}
return sum;
}

int main() {
vector<thread> threads;
double pi = 0;
for (int i = 0; i < NUM_THREADS; i++) {
threads.emplace_back([i, &pi]() {
double result = calculate_pi(i * NUM_ITERATIONS / NUM_THREADS, (i + 1) * NUM_ITERATIONS / NUM_THREADS);
pi += result;
});
}

for (auto& t : threads) t.join();

cout << "Pi is approximately " << pi / NUM_ITERATIONS << endl;
return 0;
}
2.线程池
#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <list>
#include <cstdio>
#include <exception>
#include <pthread.h>
#include "locker.h"

// 线程池类,将它定义为模板类是为了代码复用,模板参数T是任务类
template<typename T>
class threadpool {
public:
/*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/
threadpool(int thread_number = 8, int max_requests = 10000);
~threadpool();
bool append(T* request);

private:
/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/
static void* worker(void* arg);
void run();

private:
// 线程的数量
int m_thread_number;

// 描述线程池的数组,大小为m_thread_number
pthread_t * m_threads;

// 请求队列中最多允许的、等待处理的请求的数量
int m_max_requests;

// 请求队列 按理说应当叫 m_requestqueue
std::list< T* > m_workqueue;

// 保护请求队列的互斥锁
locker m_queuelocker;

// 是否有任务需要处理
sem m_queuestat;

// 是否结束线程
bool m_stop;
};

template< typename T >
threadpool< T >::threadpool(int thread_number, int max_requests) :
m_thread_number(thread_number), m_max_requests(max_requests),
m_stop(false), m_threads(NULL) {

if((thread_number <= 0) || (max_requests <= 0) ) {
throw std::exception();
}

m_threads = new pthread_t[m_thread_number];
if(!m_threads) {
throw std::exception();
}

// 创建thread_number 个线程,并将他们设置为脱离线程。
for ( int i = 0; i < thread_number; ++i ) {
printf( "create the %dth thread\n", i);
if(pthread_create(m_threads + i, NULL, worker, this ) != 0) {
delete [] m_threads;
throw std::exception();
}

if( pthread_detach( m_threads[i] ) ) {
delete [] m_threads;
throw std::exception();
}
}
}

template< typename T >
threadpool< T >::~threadpool() {
delete [] m_threads;
m_stop = true;
}

template< typename T >
bool threadpool< T >::append( T* request ){
// 操作工作队列时一定要加锁,因为它被所有线程共享。
m_queuelocker.lock();
if ( m_workqueue.size() > m_max_requests ) {
m_queuelocker.unlock();
return false;
}
m_workqueue.push_back(request);
m_queuelocker.unlock();
m_queuestat.post(); // V
return true;
}

template< typename T >
void* threadpool< T >::worker( void* arg ){
threadpool* pool = ( threadpool* )arg;
pool->run();
return pool;
}

template< typename T >
void threadpool< T >::run() {
while (!m_stop) {
m_queuestat.wait(); // P
m_queuelocker.lock();
if ( m_workqueue.empty() ) {
m_queuelocker.unlock();
continue;
}
T* request = m_workqueue.front();
m_workqueue.pop_front();
m_queuelocker.unlock();
if ( !request ) {
continue;
}
request->process();
}
}

#endif