命名空间
namespace NameSpace{}
,为解决变量以及函数重名而出现;
只能在全局中声明,可以嵌套,使用NameSpace::Items
,调用内容;
可以在函数开头用 using NameSpace::Items
,来声明引用内容,或者用 using namespace NameSpace
,来声明引用全部内容;
可以匿名,引用内容则为::Items
,等效于全局内容;
引用
定义的时候就需要赋值:int &ra = a;
,前面加地址符号,意义是使得ra和a共享地址(取个别名);
数组定义时记得加括号明确计算意义:int (&ra)[10] = a[10]
;
类似指针,函数传参时可以间接影响外部变量,并且返回时不能返回局部变量,因为是一个地址,内容会发生改变;
本质
int &ra = a
等价于 int * const ra = &a
;
目的为了简化源码理解,不需要构造多级指针,传参时传入引用可以体现出改变外部变量的意思;
面向对象与类
特征:封装,继承,多态;
封装
将一类的函数和数据装在一个类里,设置私有数据域,和公共方法称为封装;
结构体等同于类,类里的对象拥有属性(数据),行为(函数),以及构造函数(初始化数据的函数,与类同名无返回);
初始化定义:声明同时赋值;
1 | //构造函数后面加冒号变量为初始化,意义在于控制常量 |
析构函数:与类名同名,前面加~,局部变量返回时自动执行,一般做扫尾工作,如free;
调用类函数的时候,实际上编译器将外部变量的引用(匿名 / this )传入了类函数中;
定义在类中的函数,可以使用 this 指针,这个指针用于操控类里的变量,防止同名冲突;
继承
将一个类的内容添加到另一个类的起始;
1 | class Father |
相当于:
1 | class Child |
可以通过父类引用访问子类,但只能指到age结束,要访问子类后续可以用操作指针;
同样可以通过子类引用访问父类,但需要强制转换,且指针可以访问父类后面的空白区域;
1 | Child ch; |
若继承里有同名变量,则使用就近原则使用子类的内容;若要访问父类的同名变量,加上父类名:
1 | Child a; |
C++拥有多继承,按顺序排的类,哪个在前,其内容内存地址继承在最上方;
权限继承:
class修饰默认为private,若以public继承,则父类内容里保持不变(public还是public,private还是private);
private修饰的内容继承,子类不能访问,但protected可以;(protected和private就只有这个区别)
构造析构函数也会继承;
new&delete
创建类指针时,在堆开辟类内存空间并执行构造函数: Child *pch = new Child();
类似于java;
使用delete释放空间并执行析构函数: delete pch;
;
拷贝构造函数
1 | Test::Test(Test &testaddr) |
用于对象,复制属性时候执行,只是浅拷贝,值转换,指针可能出错;一般对象作为参数和返回值时就会调用;
虚继承
用于避免多继承的多义变量产生;如:A -> B,A -> C;B,C -> D;使得D里有两组A内容;
继承时,在继承类的前面用 virtual 修饰,称其为虚基类;此时,虚基类不会直接继承其内容给子类,而是会给子类一个虚基类表,这是一个指针,指向两个数据,第一个表示虚基类表位于当前所在类的偏移,第二个表示继承父类位于虚基类表的偏移;
一般虚继承的父类内容放在子类内存的下方;
友元
在类中定义,用 friend 修饰,可以为另类和函数,使其能够直接使用 private 修饰的内容;
运算符重载
类类型的对象进行运算是没有意义的,所以可以自己给运算符定义意义;
使用以下内容重载运算符 加号,使得当两个对象相加时执行 “+” 函数的语句:
1 | class A |
多态
明确一些专有名词:
- 重载:同一类里,同名函数,不同参数表;
- 重写:不同类,且类有继承关系,同名函数;
- 静态联编:程序编译时定死函数符号以及类指针的引用;动态联编则相反;
1 | class A |
可以抽象地将函数理解成是存在于类中(实际上并没有),继承后盖在子类头上,一共有两个HaHa函数,就近原则选择父类的函数(父类指针),而虚函数的声明免除了这个误会,就类似于虚继承了;
同一个指针,指向不同对象,展现出不同效果的情况,称其为多态,为了实现多态而不产生歧义,才有的虚函数;
定义:一个类中声明了虚函数,但是没给出实现,此称为纯虚函数,这个类叫做抽象类;抽象类不配有对象;
1 | class A |
Q:为什么这么做?
A:用于构造模型,用子类去实现具体内容,并由一个指向父类的指针去实现多个子类的多态;
如果定义了虚函数,则类里会存在虚表指针独占内存,指向虚表,虚表里包含各个虚函数的地址;
继承会合并父类虚表为一个,如果是多继承,则合并第一个继承的虚表,后面的会保留下来;
模板
为解决多次重载不同**类型(重点)**参数的函数而出现;
1 | template<typename DD> |
使用时,直接代入函数就行,DD需同类型,但不能指针;
需要指针时,需要使模板特化:
1 | //紧跟上面的内容写 |
如果有重载函数,模板特化,函数模板都满足使用的类型,则优先级由最特化到最不特化;
类也有模板,用 template<class DD>
声明;DD则可替换类里属性的类型;
使用时,用 ClassName<type> a;
来创建一个类对象;
模板的机制:
实际上,模板通过把一个数据类型用通用参数符号来代替,实际使用时,用某种数据类型进行替换;
达到处理类型不同,实现功能相同的效果;
模板技术成为泛型编程;
异常处理
三个关键词:
throw : 手动抛出异常,一般满足if关系式后执行,也一般存在try包括中;
1 | throw code; //code为之后的捕获catch返回的内容 |
try 和 catch 成对出现,前者会用大括号包住可能出现异常的语句块;后者作为函数跟随: catch(type code)
, 接收抛出的code,并执行catch函数里的内容;
catch里也能放 ...
,意思是捕获任意类型(包括类)异常(接收任意类型code);
1 | int func(int a, int b) |
上面代码会使得打印catch中的语句,且code值为12;
STL
标准模板库;
其部件:容器,算法,迭代器;目的是为了更好地存储数据(如排序和查找遍历)
容器
顺序容器:
Vector:向量,动态数组
可扩大数组,每次扩大自身2倍;
用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16push_back //插入到末尾
insert //指定位置插入
Pop_back //删末尾元素
erase //删除指定位置
Clear //清空所有
Vec[i] //访问索引元素
at //返回指定下标处元素
begin //返回 iterator 指向第一个元素
End //返回 iterator 指向末尾下一个
empty //是否为空?
size //获取元素个数
swap //交换两个元素
---
vector<type> item; //创建
vector::iterator it = item.begin(); //创建迭代器(指针)String:字符串
List:双向链表
不能操控下标,只能添加和删除以及遍历;
用法和维克托差不多,多了个对头的操作:
1
2
3
4
5
6Push_front //插入头部
Pop_front //头部删除
---
list<type> item; //创建
list::iterator it = item.begin(); //创建迭代器(指针)若要操作插入,需要创建迭代器,并且用for循环让iterator++,对list而言,迭代器不能直接加一个数;
Deque:双端队列
关联容器:
Set
Map
类似于python字典,有 key 和 value,其类型位pair;
可以用
map[key] = value
实现赋值;用法:
1
2
3
4
5
6
7using namespace std;
map<const char*, int> item;
item.insert(make_pair("BC",12));
map::iterator it = item.begin();
it -> first //访问key值
it -> second//访问value值Multiset
Multimap
容器适配器:
- Stack
- Queue
- Prority queue
接口的概念
在面向对象的过程中,接口就是公共属性的函数,是类内部私有属性和用户的桥梁;
在多态的解释中,接口是抽象类函数,运用这个接口去实现不同子类的多态;
总结
实际学下来花了接近2个星期吧,说多不多说少不少,要真的掌握和吃牢固还是比较难受;
这么多内容实际上和C的差别也就是编译器,底层展现的代码其实都差不多(比如引用),按java的说法,其实对于这类语言还有更多说法,比如类里定义另类型对象这种聚集关系,以及链式异常(两种不同嵌套);
关于多态的思考
类型变量实际上也是一个引用,只不过CPP是声明即创建,只不过没赋值,而java是需要声明和创建的;所以导致一个结果:java只需要声明后创建子类类型就能实现多态,而CPP需要创建一个类指针来接收子类引用;
关于类的思考
STL的思想就很像java,感觉STL在往java和python靠,把一些容器的实现都弄成类,而且是泛型的;但是用之前记得调用头文件和std命名空间;