第11章 使用类
书中课后练习将在https://github.com/linlll/CppPrimePlus发布
运算符重载
运算符重载是一种形式的C++多态。这符合C++语言的特性——面对对象,C++语言有很多我们自己定义的数据类型,那当我们想要进行基本运算的时候,会发现有一定的局限性,就比如说,在C语言中,想要实现两个等长数组的各个元素相加,我们只能使用for或者while循环进行相加并赋值,但是在C++中,我们可以定义一个表示数组的类,在类中可以重载运算符,对其进行你想要的操作,在使用的时候,表示法和基本的运算符是一样的操作,例如arr1 = arr2 + arr3
,要重载运算符,需使用被称为运算符函数的特殊函数形式,格式如下:
operatorop(argument-list)
// eg
operator+();
operator*();
具体做法看下一节
计算时间:一个运算符重载示例
让我们看看不使用运算符重载的一个示例,这是一个表示时间的类。
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
const Time Sum(const Time & t) const;
void Show() const;
};
上面是一个表示时间的类的声明,有相加,重置等运算,再看重载运算符后的类的声明。
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time operator+(const Time & t) const;
void Show() const;
};
可以看出对之前的Sum函数进行修改,利用运算符重载进行重新定义函数,使用这种函数的时候,我们可以使用函数表示法和运算符表示法,例如
Time a(1, 30);
Time b(2, 20);
Time c = a.operator+(b);
Time d = a + b;
其中,最后两句的效果是一样的。
但是对运算符重载是有限制的
请看原文。
友元
类介绍到这里,我们都知道类中的私有成员是不可以外界访问的,只能通过类中的共有成员对其进行访问,也就是说只有作用域在类中的成员才能访问,作用域不同是不能访问其私有成员的,但是这种限制太过严格,将会不适合特定的编程问题。C++提供了友元访问权限,这将使得我们可以访问类中的私有成分。
友元分为三种:
- 友元函数
- 友元类
- 友元成员函数
本章先讲友元函数。
重载*运算符
为什么需要友元呢?我们在重载二元运算符的时候就常常需要友元。例如之前提到的Time类,重载了一个乘法运算符,运算写法为A = B * 2.75
,可以看到A和B都是类,但是2.75为一个基本数据类型double,这样的写法是合理的,因为编译器会将其认为A = B.operator*(2.75)
,也就是说,这个乘法运算是调用类B中所重载的那个乘法。但是可否写成A = 2.75 * B
呢,答案是不行,因为,2.75是一个基本类型,并没有任何的函数,所以我们必须重载一个以double为第一参数的乘法函数,满足客户需求。
那么函数原型就出来了:Time operator*(double m, const Time & t)
,其用法为:A = 2.75 * B; A = operator*(2.75, B)
,那么问题来了,这个函数原型是不能访问Time类中的私有成员的,从而导致这个函数根本不能使用,那么就有了友元函数了,友元函数可以大肆访问类的私有成员。
创建友元函数需要在函数原型面前加上关键字friend,例如
friend Time operator*(double m, const Time & t);
该原型意味着下面两点:
- 虽然operator*()函数是在类声明中声明的,但是他不是成员函数,因此不能使用成员运算符来调用
- 虽然operator*()函数不是成员函数,但他与成员函数的访问权限相同。
因为operator*()函数不能算是类中的成员函数,所以它的作用域不是这个类,也就是说在定义的时候不需要加上作用域限定符::,另外,在函数定义的时候不能够加上friend关键字。
实际上对于这个乘法重载,也可以写成一个非友元函数
例如
Time operator*(double m, const Time & t)
{
return t * m;
}
这实际上是利用了类中的成员函数。
重载<<运算符
class Time
{
...
friend std::ostream & operator<<(std::ostream & os, const Time & t);
}
重载运算符:作为成员函数还是非成员函数
看原文
再谈重载:一个矢量类
看原文
类的自动转换和强制类型转换
当构造函数接受一个参数的时候,我们可以使用构造函数进行显式或隐式的类型转换。也可以通过在类中定义一个转换函数进行类型转换,但是在转换类型的时候必须考虑二义性,编译器一旦发现二义性的问题,便会报错。