【c++】【基础】【primer_plus】【第十一章】使用类

运算符重载

运算符重载是多态的一种形式。指c++允许赋予同一运算符多种含义。

运算符重载可使自定义类型(类等)看起来更像基本的数据类型。

使用 operator声明重载函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma once

class Point{
private:
double m_x;
double m_y;

protected:

public:
Point();
Point(double x, double y);
~Point();

void set_x(int x) { m_x = x; }
double get_x() const { return m_x; }

void set_y(int y) { m_y = y; }
double get_y() const { return m_y; }

Point operator+(const Point& b) const {
Point r(m_x + b.m_x, m_y + b.m_y);
return r;
} // 重载+运算符

};

int main() {

Point x(1, 2), y(3, 4);
Point z = x + y;
cout << z.get_x() << " " << z.get_y() << endl;

return 0;
}

调用 z = x + y; 相当于 z = x.operator+(y);

重载限制

重载后的运算符必须至少一个操作数是用户自定义类型。

使用运算符重载不能违反运算符原来的句法规则,如操作数的个数和优先级等。

不能创建新运算符。

有些运算符不能重载。

有些运算符只能通过成员函数重载,而不能通过非成员函数重载,如下。

1
= () [] ->

非同类型重载

1
2
3
4
5
6
Point operator*(double v) const {
Point r(m_x*v, m_y*v);
return r;
}
Point x(1, 2);
Point z = x*2.0;

友元函数

上面的语句z = x*2.0;会转换成z = x.operator*(2.0);,但是若语句为z = 2.0*x;呢,就无法达到与上式一样的效果。

一个解决办法是使用非成员函数。

1
Point operator*(double a, const Point& b);

那么将面临的问题是这个函数必须可以访问Point类的私有成员。

可以使用友元函数来解决这个问题。

友元函数可以访问类的私有成员,是一类特殊的非成员函数。

声明

友元函数的原型放在类中,但不是类成员函数,不用使用::调用。

前加friend关键字,该关键字只在声明中加,定义中不加,表示其可访问声明位置所在类的私有成员。

1
friend Point operator*(double a, const Point& b);

定义

友元函数的定义不用加friend关键字,也不用加类名::限定符。

这之后,z = 2.5*a;会转化为非成员函数z = operator*(2.5, a);

使用

友元重载和成员函数重载只可选择一种。有时候只能用友元,如第一个参数不能是类对象时。有时候二者皆可但友元重载会更好。

有一种常用的友元函数为重载<<(重定向)运算符。

1
friend ostream& operator<<(ostream& out, const Point& a);

改进后的Point类如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#pragma once
#include <iostream>
using std::ostream;

class Point{
private:
double m_x;
double m_y;

protected:

public:
Point();
Point(double x, double y);
~Point();

void set_x(int x) { m_x = x; }
double get_x() const { return m_x; }

void set_y(int y) { m_y = y; }
double get_y() const{ return m_y; }

Point operator+(const Point& b) const;
Point operator*(double v) const;
friend Point operator*(double a, const Point& b);

friend ostream& operator<<(ostream& out, const Point& a);

};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "Point.h"
#include <iostream>
using std::cout;
using std::endl;

Point::Point() {
m_x = 0.0;
m_y = 0.0;
}

Point::Point(double x, double y) {
m_x = x;
m_y = y;
}

Point::~Point() {
cout << "point " << m_x << " " << m_y << "is deleted\n";
}

Point Point::operator+(const Point& b) const {
Point r(m_x + b.m_x, m_y + b.m_y);
return r;
}

Point Point::operator*(double v) const {
Point r(m_x*v, m_y*v);
return r;
}
Point operator*(double a, const Point& b){
Point r(a*b.m_x, a*b.m_y);
return r;
}

ostream& operator<<(ostream& out, const Point& a) {
out << a.m_x << " " << a.m_y;
return out;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "Point.h"
using namespace std;

int main() {

Point x(1, 2), y(3, 4);
Point z = x + y;
cout << z << endl;

Point zz = 2.0*x;
cout << zz << endl;

cout << z << endl << zz << endl;
return 0;

}

转换函数 -- 类的转换

其他类型转换到类

只含有一个参数的构造函数可以作为转换函数,进行自动(隐式)转换。

1
2
3
Point(double v) { m_x = m_y = v; }      // Point类的一个构造函数
Point t = 12.0; // 自动转换
// 相当于 Point obj = Point(12.0);

有时候并不需要上述自动转换,可在函数原型(定义中不需要)前添加explicit关键字。

1
explicit Point(double v);

但是仍然可以使用显示转换。

1
Point t = Point(12.0);

1
Point t = (Point)12.0;          // 旧式类型转换

当定义了Point(double);之后,e = 12.0 + x;即使不使用友元函数也可运行,因为12.0会隐式地转换为Point类型。

类转换到其他类型

可使用operator关键字声明转换函数,用来将该类转换为其他类型。

1
2
operator type_name();
operator double();

转换函数必须是类方法,且不能指定返回类型,不能有参数。

1
2
3
4
5
6
// Point的一个成员函数
operator double() const;
Point::operator double() const { return m_x + m_y; } // 定义

Point t; double v = t; // 隐式转换
double v = double(t); // 显式转换

注意不要存在二义性。

c++11之后,explicit也可用于转换函数,以防止隐式转换。

1
2
explicit operator double() const;
double v = t; // 如果加了explicit,则此句会报错