C++面向对象高级编程(下)笔记 WEEK3

GeekBand  by  494631002

C++面向高级编程(下)与(上)不同,重点在于泛型编程和面向对象编程;


通过这个星期的课程,再次让我体会到了在C++中操作符重载的重要性;并且有很多特性的关键位置都由操作符的重载函数来实现;


一、转换函数(Conversion Function)

C++面向对象高级编程(下)笔记 WEEK3_第1张图片
黄色部分是转换函数

在上图中,蓝色的部分是Fraction类的构造函数;黄色部分是转换函数;在方框底下,创建了一个Fraction类型的对象,调用了构造函数。当执行下一条语句的时候,由于‘+’号的两边并不是同一个类型,因此编译器率先会去寻找有没有‘+’号的重载;但是并没有发现类中有‘+’号重载的成员函数,但是看到了黄色的这部分,所以将f转换成了double类型的对象再相加;

注意,我刚才说的是转换成double类型的对象。在这个例子中只是把f转换成基本类型double(基本类型包括int、double、char...),其实还可以转换成其他自定义的类型,那就要看你自己怎么实现自定义的类和如何转换了;

当然,要实现分数的加法也可以这样写,通过操作符重载,告诉编译器'+'号两边类型不一致时该怎么处理:

C++面向对象高级编程(下)笔记 WEEK3_第2张图片
另一种实现分数加法的方法

从这种方法中,我们可以看到有‘+’号的重载;的确,编译器也会执行重载函数,只不过‘+’号的重载是作用在‘+’号右边的操作数,并且这个对象的类型必须是分数,而在实际的操作中,4并不是分数类型;这个时候,编译器会执行构造函数,把4转换为4/1;

我们可以发现,蓝色部分的构造函数有些特殊;它接受两个参数,但是其中一个参数已经有了自己的默认值,也就是说,在实际创建对象的时候,我们可以只设置一个参数的初值,另一个可以初始化也可以不用初始化;

显然,当我们把两者写在一起的时候,编译器会报错,因为编译器发现有两种情况可以选择:

C++面向对象高级编程(下)笔记 WEEK3_第3张图片
ambiguous,有歧义的

在这样的情形下,我们使用explict关键字,告诉编译器不要用构造函数来进行对象的类型转换;通常explict用在构造函数之前。

C++面向对象高级编程(下)笔记 WEEK3_第4张图片
explict关键字的正确使用

这里,编译器依然去先寻找'+'号的重载,可是这里'+'号的重载并不符合要求(+号右边需要一个分数类型),因此f会通过转换函数转换成了double类型和4相加,可最终,无法将这个结果(double类型的对象)转换成分数类型给d2,编译器报错。

二、智能指针(Pointer-like classes)

智能指针,其实是用类来实现,这种类产生的对象和普通指针在功能上相比,多了一些新的功能;

还是先考虑使用者会如何去使用,需求就是要求:

C++面向对象高级编程(下)笔记 WEEK3_第5张图片
右边灰色部分表示使用者动作

也就是说,这种智能指针类中,必定会包含普通的指针,实现基本的功能;

因为我们是把智能指针用于未知的类型的对象,因此我们在设计的时候应该采用模板;结合这个具体的实例来说,右边使用者先创建一个指向Foo类型对象的指针的智能指针sp; 这里我觉得是智能指针的对象sp和Foo对象(临时对象)一同被创建出来。sp指向这个临时对象;

随后将使用指针的基本用法,解引用'*',用于取得sp所指的内容;至于图中的f,并不是清楚是干什么的;总之,我们得先设计'*'操作符的重载了;另外还有'->'的重载,这个的设计需要花点时间来思考;我们知道'->'是用来取一个结构体中的成员的,'->'其实是对它左边的对象进行操作,返回的是指针类型,'->'的左边必须是一个指针;既然sp已经指向一个对象了,所以这里使用'->'就是获取这个对象中的一个成员,这里就是调用成员中的一个方法;显然,'->'的设计如图所示;

到这里,我们成功的实现了智能指针的基本功能;即以后,我们可以直接创建某个类的指针了;以后希望使用类中的某个函数,也可以通过指针来直接调用;还有更多的功能,比方把指针当做参数来传递等等。。。只要基本指针都能做的,这个智能指针也可以做。


三、仿函数(Function-like class)

如果在一个class中看到有关于'( )'的重载,那么这种类产生的对象就是函数对象;

C++面向对象高级编程(下)笔记 WEEK3_第6张图片
仿函数

图中的左半部分,我们可以看到了这三个类都有对'( )'进行重载,因此这三个类产生的对象都可以当做函数来使用;select1st在暗示需要传入Pair类型的对象(Pair类在图的的右边,里面有两个其他类的对象的成员变量),里面的构造函数如图所示;'( )'重载就是去取出Pair类中的第一个对象;

标准库中有很多仿函数,其实这些仿函数都是一个个的类。


四、类模板

类模版中可以指定类中函数参数或者成员变量的类型为模版类型:

类模板是这样一种通用的类:在定义类时不说明某些数据成员、成员函数的参数及返回值的数据类型。类是对对象的抽象,而类模板是对类的抽象,及更高层次上的抽象。类模板称为带参数的类,也称为类工厂(class factory),它可用来生成多个成员相同而某些数据成员、成员函数的参数及返回值的数据类型不同的类型参数。

C++面向对象高级编程(下)笔记 WEEK3_第7张图片
每次用的时候,在类名称后面加上,就可以把类中所有的T变成typename


这样做的原因是因为你可以省去了大部分重复的代码。而且事先我们并不能确定所创建的对象中,每个成员的类型是什么,所以最好就是当你创建一个对象的时候再去使用模板就行,这时编译器会自动把所有的T替换为需要的类型。


五、函数模板


1.函数模板是一种不说明某些参数的数据类型的函数。

2.函数模板被调用时,编译器根据实际参数的类型确定模板参数T的类型,并自动生成一个对应的函数。

3.定义函数模板时也可以使用多个类型参数,这时,每个类型参数前面都要加上关键字class 或typename,其间用逗号分隔。

C++面向对象高级编程(下)笔记 WEEK3_第8张图片
函数模板

和之前的类模板类似,只需要把传入的参数类型,函数的返回类型都设置为T,等到时候调用的时候编译器会自动推导出参数类型。

补充:

函数模板和函数模板的区别:请参考CSDN《函数模板与类模板的区别》

函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。

注意:模板类的函数声明和实现必须都在头文件中完成,不能像普通类那样声明在.h文件中实现在.cpp文件中:

C++面向对象高级编程(下)笔记 WEEK3_第9张图片
【转载图片自CSDN】


C++面向对象高级编程(下)笔记 WEEK3_第10张图片
转自百度


六、成员模板

定义:任意类(模板或非模板)可以拥有本身为类模板或函数模板的成员,这种成员称为成员函数模板。

C++面向对象高级编程(下)笔记 WEEK3_第11张图片
刚刚Pair类中所省略的一点东西

也就是说,当T1、T2确定下来之后,U1、U2还可以继续当模板;

这里比较抽象,这里我们需要结合实例来说明:

C++面向对象高级编程(下)笔记 WEEK3_第12张图片
成员模板的例子

这个例子中,我们创建了四个类;其中的继承关系如右上角所示;

而pair类的每一个对象是由两个其他类的对象所组成的;也就是说,类中有类;

我先把所有的T1,T2进行替换:

C++面向对象高级编程(下)笔记 WEEK3_第13张图片
T1,T2替换

现在,代码变得非常清晰了:

第5行是新建一个Derived1和Derived2的临时变量,并把临时变量的值作为first和second的初值;第6行是另外一种构造函数,只不过参数不是默认值;

但是,为了实现用p初始化p2,又要设计新的构造函数了;

C++面向对象高级编程(下)笔记 WEEK3_第14张图片
将U1,U2替换


总结:模板有三种:类模板、函数模板、成员模板;


七、模板的特化和偏特化

特化是泛化的反义词,偏特化是局部特化的意思;偏特化有两种情形:

C++面向对象高级编程(下)笔记 WEEK3_第15张图片
偏特化——个数的偏;一般是在多个模板中制定特定的类型


C++面向对象高级编程(下)笔记 WEEK3_第16张图片
范围的偏:缩小至一个特定的类型,但是不够具体

八、零碎的一些概念

auto:

在变量前面加上auto,可以让编译器去推导类型,但是需要注意的是,一定要用在定义的语句前,如果是在声明的语句之前加上auto,那么编译器是无法推导的;

ranged-base  for :

C++面向对象高级编程(下)笔记 WEEK3_第17张图片

你可能感兴趣的:(C++面向对象高级编程(下)笔记 WEEK3)