1.转换函数
在执行Fraction f(3,5)时,使用到了重载的操作符(),这样就将f转化为0.6。
在使用转化函数时应当注意以下几点:
(1)转换函数是不能有参数的;
(2)转换函数是不需要指定返回类型的,编译器将自动赋予其返回值类型;
(3)在一个类中可以有多个转换函数。
在上图中,没有重载操作符(),而是对操作符+做了重载。在其中使用了Fraction& f。Fraction类的构造函数是一个non-explicit-one-argument ctor (无explicit 的单实参构造函数)。由于存在默认参数值,所以此构造函数实际上只需要一个参数就行。
在使用代码段中,没有将f(3,5)转换为0.6,因为在类中未对()操作符进行重载,而在执行+操作时使用了重载的+操作符,因而将4转化为4/1。
此过程可以看成是第一类转换函数的逆过程。
那么如果两者都被使用时,那么就会出现二义性。是将f(3,5)转换为0.6?还是将4转换为Fraction(4,1)?两者无法确定!
为此C++提供了关键字:explicit。通过explict指定了转换函数。避免出现二义性错误。注意:explicit应用于构造函数的前面。
2.智能指针
智能指针是由一种类类型引出的:pointer-like classes,一个类被设计得像一个指针。
智能指针的构造函数 shared_ptr (T* p):px(p){},其中必然带着一个普通指针。指针所允许的动作,所创建出的class也要允许。例如 shared_ptr支持对*号和箭头的重载。
迭代器也是另外一种智能指针,与此同时迭代器所要处理的操作更多,如*、箭头、++、--等,以满足迭代器更多功能的要求(例如对容器的遍历等)。
3.仿函数
在C++中能够支持()操作的对象都被称之为function-like对象。
在上图中三个结构体都对()操作进行了重载,一次均为仿函数。在上图中灰色区域部分为仿函数类的父类。
应当注意:在标准库中,仿函数所使用的奇特的base classes的理论大小为0,实际得到的可能为1。
4.namespace
namespace是为了避免出现名称冲突。其基本结构如下:
namespace jj01{
function1(){...};
}
使用时:jj01::function1();
不同的namespace的要素名称可以相同。
5.模板
5.1 类模板
课件中的类中的复数的实部与虚部的数据类型不定,数据类型在使用时确定。
5.2函数模板
函数模板要注意实参推演。实参推导出的类型应当保证能够支持相对应的操作。在编译时,会编译两次。首次会检查其中是否包含语法错误。第二次编译则会是在模板被使用的时候,保证模板中所定义的操作能够被正确支持。
5.3成员模板
就是讲一个模板作为另一个模板的成员。
在使用成员模板时应当注意其继承关系。
5.4模板特化
特化过程指定了特定的数据类型。
template<>struct hash
{
// 重载()操作符
size_t operator()(int x){return x};
}
// 使用
cout<
()(10);
以上所述为模板的全特化,还存在模板的偏特化:
(1)个数的“偏”:对部分模板参数进行参数类型的绑定;
(2)范围上的“偏”:例如由接受任意类型变为只接受指针。
5.5 模板模板参数
XCls
6.variadic templates 数量不定的模板参数
上述代码的过程相当于一次递归调用。
7.auto
在其中编译器自动推演返回类型。应当注意:并不是所有的变量都是用auto。除非每次声明变量都进行赋值。否则就会出错。
8.ranged-based for
数据必须是从容器中取值(注意:{}是天然的容器)。在上述代码中应当记住以下:
pass by value:乘以3不会影响原数据。
pass by reference:乘以3会影响原数据。
9.reference
应当注意以下几点:
(1)int& r = x; //r代表x,r与x都是0,不是一个指针;
(2)reference必须要有初值。reference指定后就不能改变;
(3)int x2 = 5;
r = x2;// r不能再代表其他值,r此时代表的是x,所以当前r与x都是5。
reference通常不用于变量的声明,而是常用于对参数类型和返回类型的描述。