期末速成C++【知识点汇总完】
目录
第一章
C++特点
命名空间-命名冲突
引用
new和delete
堆和栈
缺省参数
重载/隐藏/覆盖
初始化方式
第二章
面向对象的三大特征
成员变量
成员函数:构造函数和析构函数
访问权限和继承方式
空类
常const
静态static
友元friend
第三章
重载
重载为成员函数
类型转换运算符重载
函数运算符的重载
第四章-子类父类
第五章-virtual多态
第六章-模板
第七章-STL
第八章-文件IO
第九章-异常
第十章❗
第一章
C++特点
同时支持四种编程范式,面向过程、面向对象、泛型编程、函数式编程
同C语言相比,适合开发大型应用程序(操作系统内核还是C语言)(多人同时开发namespace)(存在OOP)
具有可复用、可维护、可扩展、灵活性好的特点(活字印刷)
头文件:不带.h math.h(cmath)string.h(cstring)
关键字:都是小写。
命名空间-命名冲突
- const char* char const*:常量指针:const 修饰的是指向的内容
- char*const:指针常量:const修饰的是指针(指向的关系)
const char* p=”hello”;
char const* p=”hello”;
数组名 char arr[10];arr的实际类型是char* const
引用
函数参数
- 实参就是形参;参数类型就是常引用类型。const vector<int>&
- 常引用是常对象,常对象只能调用常函数const。重载自定义类型的operator<,常函数。(重载运算类的不能const)
函数返回值
不能返回局部变量的引用
ostream& operator<<(ostream& out,const ......&){ return out; } return *this
- 前置++返回*this:是&
- 后置++(int)返回是temp:不能是&
- +重载,返回的是局部变量:不能是&
new和delete
new/delete和malloc/free不能混用
new/delete和new[]/delete[]是两对运算符
new[]/delete[]定义变长数组
int len=100; //变量
Int* p=new int[len] //变长数组一定要用delete[]释放
p[i]和*(p+i)是等价的
堆和栈
堆(malloc/new)
- 一个进程只有一个堆。
- 手动分配,手动回收。
- 不能通过变量访问,只能通过指针间接访问。
- new/delete
- int* p = new int //两段内存:栈:p☞堆:int
栈
- 每个函数的每一次运行,都会有一个独立的栈。
- 放局部变量。
- 自动分配,自动回收
- 通过局部变量访问。
RAII资源获取即初始化Resource Acquisition is Initialization
在构造函数中去new,在析构函数中使用delete
缺省参数
- 优先写在声明里(定义不在写)(只能写一次)
- 函数在给半缺省参数,必须是从右往左连续依次给出,不能间隔跳跃。(从第一个开始)
- 调用函数传参:必须从左到右连续传参,不能跳跃。(从第一个开始)
int add(int a=1,int b=2,int c=3) {return a+b+c;}
add(1); 1 2 3
add(1,1); 1 1 3
add(1,1,1); 1 1 1
❌void Func2(int a, int b = 10, int c)
❌void Func2(int a=10, int b, int c = 20)
重载/隐藏/覆盖
重载:同名函数,同一作用域,参数列表不同。
Int add(int a,int b);
Int add(int x,int y);//二者不构成重载
隐藏:同名函数,不是覆盖都是隐藏(不是virtual虚函数),父类子类。
class Base
{
Int foo(int);//函数1
Int fool(int,int);//函数2
};
Class Derived;public Base
{
int fool(int,int);//函数3
};
1和2重载
1和3隐藏
2和3隐藏
覆盖:同名函数,多态,虚函数virtual,原型严格一致。
class Base
{
virtual Int foo(int,int);//函数1
};
Class Derived;public Base
{
virtual int fool(int,int);//函数2
};
函数1和2覆盖
Base * pb=new Derived;
pb->foo(); //函数2
初始化方式
int a=100; //C语言
int b(100); //经典C++
int c{100}; //现代C++,统一初始化
初始化的次数❓
Class foo{};
Foo a(1);//1
Foo a=1;//1
Foo a{1};//1
Foo* pf;//没有
Foo pf[10];//10次
Foo* pf[10];//一次没有
第二章
面向对象的三大特征
- 封装:隐藏内部实现
- 继承:实现代码复用
- 多态:改写对象行为
- 类中成员:成员变量(名词性的属性)和成员函数(动词性的行为)
- 重载:代码间接,❌不是代码复用。
成员变量
成员可以是自身类型的指针或引用,不能包含自己。
class Foo{
Foo *pf;
Foo& rf;
Foo f;//❌
};
成员函数:构造函数和析构函数
构造函数和析构函数和Seter/Geter
- 构造函数:无参构造函数/带参构造函数/拷贝构造函数/类型转换构造函数
- 析构函数
构造函数的参数不能是自身类型。
析构函数:释放对象
析构函数的释放顺序和创建顺序相反
class MyString{
};
MyString() //无参构造函数
MyString(string str); //带参构造函数
MyClass(const MyString& other); //拷贝构造函数
~MyString(); //析构函数
virtual ~MyString(); //虚析构函数(父类是子类可以省略virtual)
MyString(const char* str);//类型转换构造函数
构造函数:创建和初始化对象
默认构造函数
无参构造函数
默认拷贝构造函数:系统提供,按位拷贝,bit-by-bit,无参
拷贝构造函数:一个参数,自身类型的引用(包括常引用,一般是常引用)
类型转化构造函数:一个参数,不是自身类型,也不是自身类型的引用
1.无参构造函数调用的方式:
class Foo{
Foo();
};
Foo a=10;
Fool* pb=new foo;
Foo f;
Foo af[10];
2.带参构造函数调用方式:
class Foo{
Foo(int other);
};
Foo a=10;
Foo* pb=new foo(10);
Foo f(10);
Foo a[3]{1,2,3};
3.拷贝构造函数
class Foo{
Foo(const Fool& other);
};
Foo a=10;
Foo a;
Foo b=a; //自身类型的引用
Foo b(a);
Foo b{a};
4.类型转换构造函数
class Foo{
Foo(int x);
};
Foo a=10;
访问权限和继承方式
访问权限:4中访问权限,关键字有3种
默认访问权限:class是私有的,struct是公有的
不可访问成员 冻结财产
私有成员private 个人财产
保护成员protected 家族财产
公有成员public 公有财产
继承方式对访问权限的影响:三种继承方式,关键字3个,和访问权限公用一套
私有的和不可访问都是不可访问
继承方式决定最高访问权限
默认继承方式是私有的,最常用的继承方式都是公有继承
成员变量一般都是保护成员(子类访问父类,父类设为家族财产)
空类
class foo{
};
默认构造函数
默认拷贝构造函数:bit-by-bit
默认赋值运算符:bit-by-bit
默认析构函数
非静态成员函数,有一个默认的this指针。
常const
修饰各种类型☞const char* p char const*p char* const p
常函数:修改类的成员函数
常对象只能调用常函数
class Foo
{
void F() const;
};
void Foo::F() const {}
静态static
静态成员属于类
静态成员变量:必须类外初始化,不需要再写static
静态成员函数:静态成员函数没有this指针
静态成员函数:不能访问非静态成员,只能使用静态成员
两种调用方式,通过对象调用,通过类名调用
没有对象,也可以调用静态成员函数
静态成员函数 只能 调用静态成员(静态成员函数、静态成员变量)无this指针
不能调用非静态成员(普通成员变量,普通成员函数)
非静态成员可以调用静态成员?可以
友元friend
破坏封装性。一般,同一模块内部使用友元,模块内部开的后门。
可以访问类的所有成员(只能❌全部、仅仅)
不是成员函数,和访问权限无关。
单向性、不能传递、不能继承。
class Foo{
private:
friend void Bar();//不是成员函数,只是声明。
};
互为友元函数
class A{
friend class B
};
class B{
friend class A
};
第三章
重载
- 运算符重载的本质是函数重载,函数名是operator运算符。
- 重载:是为了代码间接,不是代码复用❌,不是所有运算符能被重载❌。
一般都是成员函数,
两种形式:成员函数重载,全局函数重载(往往被声明友元函数,不是必须是友元函数❌)
输入输出运算符>> <<必须是全局函数重载,声明友元函数。
friend ifstream& operator>>(ifstream& ifs,vertor<*Student>& all>);
friend iostream& operator<<(iostream& ios,const MyString& other);
= () [] ->只能被重载为成员函数
<< >>只能被重载为全局函数,声明为友元函数
前置++返回*this:是&
后置++(int)返回是temp:不能是&
+重载,返回的是局部变量:不能是&
重载为成员函数
- 左操作数必须是当前类型
- 一元运算符不需要参数,当前对象是操作数。
- 二元运算符只要一个参数,代表右操作数,当前对象是左操作数。
- = () [] ->只能被重载为成员函数
类型转换运算符重载
- operator目标类型();
- operator flaot();
- static_cast<目标类型>(表达式);
- explicit关键字,避免默认类型转换
operator float();
static_cast<float>(a);
explicit operator float();
函数运算符的重载
- 函数指针
- 仿函数
- 内置仿函数
- Lambda表达式
- 自定义类型重载<运算符
第四章-子类父类
基类Base(父类)
派生类Derived(子类):子类对象拥有父类的所有成员(但是构造和析构函数也独有)
C++支持多继承
UML的类图:三格矩形,箭头☞表示继承(子类指向父类)
析构函数调用的顺序:基类☞成员对象☞派生类
析构函数调用的顺序:派生类☞成员对象☞基类
class A{};//父类
class B:public A //子类
{
A a; //成员对象
};
B b(a); //构造:A->a->B 析构:B->a->A
- 子类对象给父类对象赋值✔
- 父类对象给子类对象赋值❌
Base b;//父类
Derived d;//子类
d=b;//❌
b=d ;//父类指针指向子类对象
Base* pb = new Derived;
Base& rb=d;
第五章-virtual多态
- virtual实现机制:虚函数表。(实现动态多态以牺牲性能为代价)
重载调用机制:取决于不同参数列表
隐藏调用机制:取决于变量类型
覆盖调用机制:取决于对象类型(运行结果)
如何调用被隐藏的父类的成员函数(通过Base::Fun,通过域操作符)
同名函数因为上下文不同会有不同的实现的一种机制
静态多态:函数重载实现,在编译阶段完成
需要花费更多编译时间,不会降低运行速度
动态多态:继承+虚函数,运行阶段完成
动态多态以牺牲性能(运行速度)为代价
纯虚函数和抽象类:
纯虚函数:虚函数 函数体=0;//都有,但是方式不一样(2)
定义了纯虚函数的类,叫做抽象类
Java:只有纯虚函数的类,叫做接口interface
抽象类不能实例化对象
- RTTI运行时类型识别:(3)不同种类各自特有的
- typeid()
- dynamic_cast()
class Base {
public:
void func() {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() {
std::cout << "Derived::func()" << std::endl;
}
};
int main() {
Derived d;
d.Base::func(); // 调用基类的 func()
d.func(); // 调用派生类的 func()
return 0;
}
// 抽象基类
class Shape {
public:
// 纯虚函数声明
virtual void draw() const = 0;
// 虚析构函数(推荐用于包含虚函数的类)
virtual ~Shape() {}
};
//3.各自特有的
if (typeid(*p) == typeid(Teacher))
{
//动态类型转化❗
Teacher* pt = dynamic_cast<Teacher*>(p);
pt->Xiaban();
}
else//if (typeid(*p) == typeid(Student))
{
Student* ps = dynamic_cast<Student*>(p);
ps->Game();
}
第六章-模板
- 模板编程也叫做泛型编程
- 目的:代码复用
- 函数是为了代码复用,子类继承父类是为了代码复用,模板编程是为了代码复用
- 函数模板,类模板:在指定类型后,变成了模板函数,模板类。
- 函数模板 => 模板函数
- 函数模板可以实例化成多个模板函数
- 高级语言中,1个函数模板,经过编译以后,在二进制代码中,会有对各对应的模板函数。
- template <typename T>(没有分号;)
- 函数类型 函数名(参数列表){函数体}
- 在没有二义性的情况下,函数模板的类型参数可以省略。
- 类模板=>类函数
- 类模板不能隐式调用,函数模板可以。
Template<typename T>
T add(T a, T b){return a+b;}
add<int>(1,2); add(1,2);
Add(1,2.3); //二义性
第七章-STL
- STL:Standard template library 标准模板库
- 三大核心:容器、迭代器、算法
- 六大组件: .................、适配器、仿函数、分配器
- 常用容器的内部实现:
- vector:动态数组
- list:双向链表
- map/set:红黑树(平衡树的一种,用于动态查找)
- vector:push_back 和 pop_back和begin 和 end
- 注意:end指向最后一个元素的下一个迭代器[ )是一个左闭右开区间
list:push_back和pop_back和push_front和pop_front- sort不能使用list
vector array deuqe随机访问迭代器- list不支持下标运算符sort不能使用
- map支持,存在插入和修改两种功能
会使用基于范围的for循环,对应的迭代器进行遍历- 基于范围的for循环
- 使用迭代器进行遍历
- for_each()遍历
构造函数:可以调用无参构造函数- vector<int>vi
- vector<int> vi(10);
- vector<int> vi(99,10);
- vector<int>vi(first,last);左闭右开的区间
//迭代器的定义:推到和自动定义- 定义迭代器的两种方式:常规和自动推导
- vector<int>::iterator itr;
- auto itr=vi.begin() //自动推导必须初始化
map<int,int>m;
map<int,int>::iterator itr;
auto itr=map.begin();
算法:
- 容器begin end实参
- sort(first,last,二元谓词)形参
- random_shuffle() 洗牌
- generate_n(back_insetor(),n,[](){});向容器中插入n个元素
- copy(,,ostream_iterator<int>(cout,间隔符));
- accumulate(,,0) 累加和
第八章-文件IO
#include<fstream>
ifstream ifs(“data.txt”,ios::in); //文本文件 打开方式
While(!ifs.eof())ifs>>容器;
ifs.close();
//控制台的输出运算符的重载:常引用
//输入运算符的重载:引用
ifstream& operator>>(ifstream& in,容器的引用list<Student*>& all){
.....;
return in;
}
第九章-异常
- try catch throw干什么?
- try catch:捕获和处理异常
- throw:抛出异常
子函数()
{ //若干层调用的子函数
throw(某个类型的异常) //抛出异常
}
主函数
{
try
{
子函数的调用;
}
catch(类型1)
{
}
catch(类型2)
{
}
//取决于数据的类型
第十章❗
auto自动类型推导
基于范围的for循环
Lambda表达式
面向对象的知识点:
父类指针指向子类对象
唯一不变的是变换,以不变应万变。
父类指针:抽象,稳定。
子类对象:具体,变换。
三大原则:封装变化点、针对接口进行编程,优先使用组合
is-a:继承和派生-代码复用
has-a:成员对象-代码复用
设计模式的来源是建筑设计领域。
SOLID五大原则:
SRP单一职责原则。
OCP开闭原则:对扩展开放,对修改关闭。(增加策略业务逻辑,❌1.增加分支(其实是修改函数,都在一个函数体)2.增加规则-增加类)
LSP里氏替换原则。
ISP接口隔离原则。
DIP依赖倒置原则:高层模块不依赖于底层模块,二者应该依赖于抽象。(高层-抽象类父类-子类_底层模块)
代码复用:函数、继承、组合、模板
使用代码简洁的语法:重载(函数重载,运算符重载),默认参数
实现面向对象:继承、虚函数、父类指针指向子类对象
inline内联函数:小函数、没有循环
Set/Get基本是内联函数
栈里的对象-调用2次构造函数 2次析构函数
class Foo
{
};
main()
{
Foo* pd=new Foo;//pd在栈里面Foo类型指针,new一个Foo类型的变量堆里面
delete pd;
Foo d1; //局部变量,对象在栈里面
}
//释放栈,释放栈里的对象,调用对象的析构
void fun(){
****************
}
inline void fun(){
****************
}//不是函数调用,直接嵌入函数体中了
main()//main函数体积变大了,以牺牲体积,提高运行速度
{
#########
fun();
#####
fun();
}
- 单选:30
- 多选:10
- 判断:10
- 填空:20
- 大题:40