C++知识点整理 by cls1277
写在前面
好像每写一个东西都爱写这一部分
(现在还没整理完,“泛型程序设计与 STL”和我想加的“附录”都还没写-2021.5.11)
本文使用Typora基于markdown由cls1277整理而成,如果有什么疑问或者错误欢迎联系我:932163311(Q) cls1277(V)
本文是我一人根据《考试内容简介》整理而成,在复习的过程中顺手整理,也为了自己以后看的时候方便。
其实说实话这些东西我也不知道有多大概率会考,整理都整理出来了怎么用看你自己了。
因为由我一人整理而成,所以不确定完全正确 ,请辩证的去看待这份整理。
你还会发现下文有很多删除线,其实是我的碎碎念罢了,所以其实不需要看哈哈哈。
每一个大部分的后面可能会有”一些小问题“这种话,是因为那是一些可能会判断对错的那种句子,建议自行检测一下会不会,不全待更。
这一部分其实我并没有给出答案,基本看自己老师的课件、课本、搜csdn等都可以搜得到。
写的过程中一个朋友跟我说,我的有的小问题在往年题里出现过,没看过往年题的我惊呆了哈哈哈
还有很多东西是我实在不想打了,就以另一种方式放在了最后(目前没有实现的附录部分)。
说是简单不想打,其实就是因为字太多了我懒得打,逃
2.1.1 模板:
模板格式:template<模板参数表>
模板参数表:类型参数 与 非类型参数
类型参数:
template<class T> / template<typename T> 这两个的效果其实是一样的
示例:
template<class T>
T qmax(T a , T b) {
return a>b?a:b; }
template<typename T>
T qmin(T a , T b) {
return a<b?a:b; }
非类型参数:template<typename T , int N>,N表示一个常量而非一种数据类型
示例:
template<typename T , int N>
bool Find(T arr[N] , T val) {
for(int i=1; i<=N; i++)
if(arr[i]==val)
return true;
return false;
}
函数模板: 格式如上文举例,此处不再赘述
类模板:
定义成员格式:
template<typename T>
class Test {
private:
T a , b;
};
定义成员函数格式:类内定义 与 类外定义
类内定义示例:
template<typename T>
class Test {
private:
T a , b;
public:
T add() {
return a+b;
}
};
类外定义:(这种方式感觉写起来好啰嗦,不太熟悉,理解不了格式就对照着例子理解)
格式:
template<模板参数表>
返回类型 类名<模板参数名表>::成员函数名(形参表) {
...函数体
}
示例:
template<typename T>
class Test {
private:
T a , b;
public:
T mul();
};
template<typename T>
T Test<T>::mul() {
return a*b;
}
实例化
函数模板实例化:函数名<函数模板实参表>(实参)
类模板实例化:类名<类模板实参表>对象名
(这一部分好像挺简单的,就不放代码了,不会哈再自己去搜搜或者问我吧)
操作符重载
一些小问题: 我们为什么要引入操作符重载? 是不是所有的符号都能重载? 哪写不能重载呢? 重载还有什么限制吗?
重载方式:成员函数 与 友元函数
成员函数:就是直接在类内写,和成员函数的写法相同,故称为一种方式
示例:
#include<iostream>
using namespace std;
class Test {
private:
int a , b;
public:
Test() {
};
Test(int aa , int bb) {
a=aa , b=bb;
}
int operator + (const Test &x) {
return a+x.a;
}
};
int main() {
Test tmp1=Test(1,2);
Test tmp2=Test(3,4);
cout<<tmp1+tmp2;
return 0;
}
友元函数:顾名思义,以友元函数的形式重载运算符
示例:
#include<iostream>
using namespace std;
class Test {
private:
int a , b;
public:
Test() {
};
Test(int aa , int bb) {
a=aa , b=bb;
}
friend int operator + (const Test &x , const Test &y);
};
int operator + (const Test &x , const Test &y) {
return x.a+y.a;
}
int main() {
Test tmp1=Test(1,2);
Test tmp2=Test(3,4);
cout<<tmp1+tmp2;
return 0;
}
下面是有助于加深理解的:
多说一句,考虑为什么要用友元函数,为什么要引入friend呢?因为在类中,类的成员的权限一般为private,所以用友元函数可以在类外调用类的成员。例如上面代码中,成员a和b为私有类型,如果不用友元函数是无法调用到a并令其相加的,也就无法重载成功。
但是,如果你还记得成员函数的定义形式的话,会记得成员函数还有一种定义方法,就是先在类内声明,在类外定义,本质其实还是成员函数,详见下例。
建议与前面两部分代码进行对比学习。
再说一点可能有疑问的地方,为什么有的地方有const 和 & 有的地方没有?其实这个可以没有是不会影响结果的。const的作用的是在调用的时候不要修改参数的值,如果函数内没有修改的话删去没有影响。&的作用是取地址做参数,如果没有修改参数的话有&比没有&的好处就仅仅是程序运行速度更快。
类内声明类外定义示例(本质:成员函数):
#include<iostream>
using namespace std;
class Test {
private:
int a , b;
public:
Test() {
};
Test(int aa , int bb) {
a=aa , b=bb;
}
int operator + (Test);
};
int Test::operator + (Test x) {
return a+x.a;
}
int main() {
Test tmp1=Test(1,2);
Test tmp2=Test(3,4);
cout<<tmp1+tmp2;
return 0;
}
单目运算符重载
这一部分比较难懂的可能就是a++和++a的重载写法。
首先得知道两个的区别,简记方法:谁在前面就先干嘛(a++中a在前面,那就先用a再自增;++a中++在前面,那么先自增再用)
先看代码:
#include<iostream>
using namespace std;
class Test {
private:
int a , b;
public:
Test(){
};
Test(int aa , int bb) {
a=aa , b=bb;
}
Test operator ++ () {
//++a 前置自增
a++; b++;
return Test(a,b);
}
Test operator ++ (int) {
//a++ 后置自增
Test tmp(a,b);
a++; b++;
return tmp;
}
void print() {
//测试输出
cout<<a<<" "<<b<<endl;
}
};
int main() {
Test m(1,2) , n(1,2);
(m++).print();
(++n).print();
return 0;
}
对照代码可以看出来,两部分其实差别不太大,那又如何区分呢?记得我老师讲过的后置自增的时候会比前置自增多一个int类型的参数,但是没用到这个参数,所以其实这个参数就是用来区分前置和后置的。至于重载函数内部怎么写就是先用先加的顺序问题,看上面的示例应该可以看懂,此处不再赘述。
双目运算符重载
这一部分其实在重载方式里面已经演示过重载+号了,可以往上面翻翻,其余的双目运算符类似。
类型转换函数重载
这一部分与前面类似且不是多么难,举个例子就可以了。
#include<iostream>
using namespace std;
class Test {
private:
int a , b;
public:
Test(){
};
Test(int aa , int bb) {
a=aa , b=bb;
}
operator double() {
return (double)a*1.0/b;
}
};
int main() {
Test m(1,2);
cout<<double(m);
return 0;
}
类模板中操作符重载
直接在第一部分(最开头)中类模板的代码中改的,重载乘号
template<typename T>
class Test {
private:
T a , b;
public:
Test() {
};
Test(T aa , T bb) {
a=aa , b=bb;
}
T operator * (Test x) {
return Test(a*x.a,b*x.b);
}
};
动态内存与数据结构
一些小问题:群体是什么啊? 数组又是啥结构呢? 动态数组怎么自己写呢?链表又是什么结构呢?能想象出来吗?
多提一句,链表好像还挺重要的,你要是问我为嘛不写在这?因为课内老师讲的跟我们(acmer)平常写的链表差距有点大……这一部分自行复习吧,我怕误导大家。
动态数组类的一种写法
(写于上机考试前)
#include<iostream>
using namespace std;
template<typename T>
class dyArray {
private:
T num[105] , *m_put;
int m_size , m_nCapcity;
public:
dyArray() {
}
dyArray(int Capcity) {
m_put = num;
m_nCapcity = Capcity;
m_size = 0;
}
void add(T x) {
*m_put = x;
m_put++;
m_size++;
}
void Print() {
for(int i=0; i<m_size; i++)
cout<<num[i]<<" ";
}
};
int main() {
dyArray<double>a(10);
a.add(1.2); a.add(0.8); a.add(0.9); a.add(1.5); a.add(9.0); a.add(0.3);
a.Print();
return 0;
}
常用的排序方法
这一部分不想赘述,可以去csdn搜索冒泡排序、插入排序、选择排序、桶排序自行学习。
也可以直接去我的csdn博客:cls1277 我曾写过排序的复习,不过码风偏acm算法竞赛不一定能看懂
查找算法:遍历查找 与 二分查找
这一部分很简单,稍微提一句就可。
遍历查找:就是把一个容器(数组)从头到尾循环一遍,并判断是否为要查找的那个值即可。
二分查找:思想易懂在高中的时候就学过二分找零点
但是真正落实到代码的话某些acm选手好像也会写挂哈哈哈
继承与派生
啊 这一部分内容很多 需要记的东西也很多 awsl
一些小问题:为什么要有继承和派生啊? 父类和子类/基类和派生类都是啥? 派生类怎么声明?(这个挺简单的,就当小问题了) 多继承时怎么声明?
公有继承/私有继承/保护继承访问权限的变化
引起的变化主要表现在派生类的成员函数和对象对基类的成员的访问控制,具体整理为如下表格:
可以访问 | public | private | protected |
---|---|---|---|
派生类成员函数 | 基类public/protected | 基类public/protected | 基类public/protected |
派生类对象 | 基类public | 无 | 无 |
Q:protected的成员究竟是什么?
A:对于类的对象而言相当于private,对派生类而言相当于public
派生类对象的构成
所谓“派生类对象的构成”无非就是构造函数、成员函数和析构函数与基类之间的关系。
构造函数:基类的构造函数不继承,需要重新声明,但是可以调用。只需要对新增的成员进行初始化,基类成员直接调用基类的构造函数
示例:
class A {
private:
int a , b;
public:
A() {
};
A(int aa , int bb) {
a=aa , b=bb;
}
};
class B:public A {
private:
int c;
public:
B(){
};
B(int aa , int bb , int cc) {
A(aa,bb);
c=cc;
}
};
上面为单继承的,多继承的其实一样去调用每个基类的构造函数。
构造函数的调用次序
三句话
1.基类构造函数:按照被继承时声明的顺序,从左向右
2.派生类成员对象:按照在类中成员声明的顺序
3,派生类中的其他内容
补充一个东西,基类在派生类中被调用可以叫做调用内嵌对象的构造函数(名词:内嵌对象!)
派生类的析构函数
调用次序与构造函数次序相反
不继承基类的构造函数,一般派生类会自动隐式调用基类的析构函数或自行声明
至于派生类的成员函数等下一部分讲
用初始化列表实现派生类构造函数
啊一听就好高大上啊 初始化列表是啥啊
代码示例,代码改自上一份派生类构造函数的代码
class B:public A {
private:
int c;
public:
B(){
};
B(int aa , int bb , int cc):A(aa,bb) , c(cc) {
}
};
二义性问题
一些小问题:什么是二义性? 什么情况下会产生二义性? 怎么声明虚基类? 虚基类有啥作用呢? 在什么时候声明虚基类呢?
解决办法:同名覆盖原则 & 虚函数 & 虚基类
虚函数在下一部分
同名覆盖原则:如果基类和派生类有同名成员,优先使用派生类,若调用基类应用::限定。
虚基类的声明示例:最好不要把成员设置为public
class A {
public:
int x;
};
class B1:virtual public A {
public:
int y;
};
class B2:virtual public A {
public:
int z;
};
class C:public B1 , public B2 {
public:
int x;
};
关于虚基类的构造函数
一个概念:最(远)派生类:通俗理解就是在整个继承派生体系中,最下层的那个类
虚基类的成员由最派生类的构造函数调用虚基类的构造函数进行初始化的。也很好理解,因为路径上的其他类都是“假的”,只有最派生类才是“真的”。
只有最派生类的构造函数调用了虚基类的构造函数。
有虚基类时多继承的构造顺序:
1.调用虚基类的构造函数,按照继承的顺序调用
2.调用非虚基类的构造函数
3.若虚基类由非虚基类派生而来,则仍按先调用基类构造函数,再调用派生类构造函数的顺序。
对于上面的第3条,代码演示:
#include<iostream>
using namespace std;
class A {
public:
int a;
A() {
cout<<"A";
}
};
class B {
public:
int b;
B() {
cout<<"B";
}
};
class C:virtual public B {
public:
int c;
C() {
cout<<"C";
}
};
int main() {
C obj;
return 0;
}
运行结果:BC
赋值兼容原则
通俗点讲就是:父类对象=子类对象; 可以用子类对象初始化父类对象的引用; 父类的指针可以指向子类
多态
一些小问题:什么是多态? 抽象类和纯虚函数都是啥? 有什么用呢?
如何实现多态?函数重载 & 运算符重载 & 虚函数
虚函数
声明:在声明函数以前写virtual而非实现时
继承性:基类设置虚函数,派生类中自动为虚函数
这里还有一个本质,因为不太理解就没敢往上写,已经联系老师了
调用:通过指针指向对象的类来决定调用哪个函数。
纯虚函数和抽象类
纯虚函数:对于暂时无法实现的函数,可以声明纯虚函数让派生类去具体实现。
抽象类:含有纯虚函数的类,只能作为基类,可以理解为一个公共接口,不能声明抽象类的对象,形式就是在虚函数后面加个=0
I/O流和异常处理
一些小问题:I/O流是什么?为什么要有异常处理?
利用文件流进行文本和二进制的读写
#include<iostream>
#include<fstream>
using namespace std;
int main() {
//文本的读写
ifstream in("in.txt",ios::in);
ofstream out("out.txt",ios::out);
in.close();
out.close();
//二进制的读写
ifstream bin("bin.txt",ios::binary);
ofstream bout("bout.txt",ios::binary);
bin.close();
bout.close();
return 0;
}
异常处理实现示例
#include<iostream>
using namespace std;
int n , m;
bool jud(int a , int b) {
if(b==0)
throw false;
return true;
}
int main() {
cin>>n>>m;
try {
jud(n,m);
}
catch(bool) {
cout<<"Error";
}
cout<<n/m;
return 0;
}
程序发现异常之后,会析构掉已经构造的。
泛型程序设计与 STL
待更
转载:https://blog.csdn.net/cls1277/article/details/116676475