c++ 笔记
1.转换函数
任何Fraction需要被转换为double类型的时候,自动调用double()函数进行转换。如上图所示,编译器在分析double d = 4 + f过程中判断4为整数,然后继续判断f,观察到f提供了double()函数,然后会对f进行double()操作,计算得到0.6,再与4相加,最后得到double类型的4.6。
2.explicit与隐式转换
上图中定义了一个类,叫Fraction,类里面重载了“+”运算符,在f+4操作过程中,“4”被编译器隐式转换(构造函数)为Fraction对象,然后通过Fraction重载的“+”运算符参与运算。
如上图所示,在Fraction中增加了double()函数,将Fraction的两个成员变量进行除法运算,然后强制转化为double类型并返回结果,在f+4重载过程中编译器将报错,可以做出如下分析:
1、首先4被隐式转化(构造函数)为Fraction对象,然后通过重载的“+”运算符与“f”进行运算返回一个Frction对象;
2、首先4被隐式转化(构造函数)为Fraction对象,然后通过重载的“+”运算符与“f”运算后对进行double运算,最返回一个Frction对象;
3、。。。
所以编译器有至少两条路可以走,于是产生了二义性,报错。
如上图所示,在构造函数Franction前加入explict关键字,隐式转换将取消,所以在执行d2 = f + 4过程中,f将调用double函数转换为0.6,然后与4相加变成4.6,由于构造函数取消隐公式转换,4.6无法转换为Fraction,于是将报错。
下图为C++ stl中操作符重载和转换函数的一个应用:
3.智能指针
下面这张图很好地说明了智能指针的内部结构和使用方法:
智能指针在语法上有三个很关键的地方,第一个是保存的外部指针,对应于上图的T* px,这个指针将代替传入指针进行相关传入指针的操作;第二个是重载“*”运算符,解引用,返回一个指针所指向的对象;第三个是重载“->”运算符,返回一个指针,对应于上图就是px。
迭代器也是一种智能指针,这里也存在上面提到的智能指针的三个要素,分别对应于下图的红色字体和黄色标注部分:
下面将仔细分析迭代器重载的“*”和“->”重载符:
创建一个list迭代器对象,list::iterator ite;这里的list用于保存Foo对象,也就是list模板定义里的class T,operator*()返回的是一个(*node).data对象,node是__link_type类型,然而__link_type又是__list_node<T>*类型,这里的T是Foo,所以node是__list_node<Foo>*类型,所以(*node).data得到的是Foo类型的一个对象,而&(operator*())最终得到的是该Foo对象的一个地址,即返回Foo* 类型的一个指针。
4.仿函数
重上图可以看到,每个仿函数都是某个类重载“()”运算符,然后变成了“仿函数”,实质还是一个类,但看起来具有函数的属性。每个仿函数其实在背后都集成了一个奇怪的类,如下图所示,这个类不用程序员手动显式声明。
标准库中的仿函数也同样继承了一个奇怪的类:
这个类的内容如下图所示,只是声明了一些东西,里面没有实际的变量或者函数,具体的内容将在STL中讨论。
5.namespace
6.类模板
7.函数模板
与类模板不同的是,函数模板在使用是不需要显式地声明传入参数类型,编译器将自动推导类型。
8.成员模板
成员模板在泛型编程里用得较多,为了有更好的可扩展性,以上图为例,T1往往是U1的基类,T2往往是U2的基类,可以看下面这个例子:
通过这种方法,只要传入的U1和U2的父类或者祖类是T1和T2,那么通过这样的方式可以实现继承和多态的巧妙利用,但反之就不行了。这样的方式在STL中用得很多:
9.模板偏化
正如其名,模板偏化指的是模板中指定特定的数据类型,这和泛化是不同的:
当然,模板偏化也有程度之分,可以部分类型指定,称之为偏特化:
10.模板模板参数
11.可变数目模板参数
过多内容将在C++11课程中讲解,这里暂时只做介绍。
12.auto关键字和增强型for循环
过多内容将在C++11课程中讲解,这里暂时只做介绍
13.reference
reference可以看做是某个被引用变量的别名。
14.虚指针和虚函数表
如上图所示,定义了三个类,A、B和C,B继承于A,C继承于B,A中有两个虚函数,B中有一个,C中也有一个。编译器将A的对象a在内存中分配如上图所示,只有两个成员变量m_data1和m_data2,与此同时,由于A类有虚函数,编译器将给a对象分配一个空间用于保存虚函数表,这张表维护着该类的虚函数地址(动态绑定),由于A类有两个虚函数,于是a的虚函数表中有两个空间(黄蓝空间)分别指向A::vfunc1()和A::vfunc2();同样的,b是B类的一个对象,由于B类重写了A类的vfunc1()函数,所以B的虚函数表(青色部分)将指向B::vfunc1(),同时B继承了A类的vfunc2(),所以B的虚函数表(蓝色部分)将指向父类A的A::vfunc2()函数;同样的,c是C类的一个对象,由于C类重写了父类的vfunc1()函数,所以C的虚函数表(黄色部分)将指向C::vfunc1(),同时C继承了超类A的vfunc2(),所以B的虚函数表(蓝色部分)将指向A::vfunc2()函数。同时上图也用C语言代码说明了编译器底层是如何调用这些函数的,这便是面向对象继承多态的本质。
15.this指针
this指针其实可以认为是指向当前对象内存地址的一个指针,如上图所示,由于基类和子类中有虚函数,this->Serialize()将动态绑定,等价于(*(this->vptr)[n])(this)。可以结合上节虚指针和虚函数表来理解,至于最后为什么这样写是正确的,下面小结将会解释。
16.动态绑定
17.重载delete和new操作符
18.重载delete()和new()操作符