主要内容:
运算符重载(成员函数(第一个操作数用作调用成员函数的对象)、友元函数(操作数与函数参数比对))
友元函数(接口扩充)
类型转化:自动类型转化(构造函数、转化函数实现)
运算符重载:
我们的目标使得用户定义类型和基本类型的用法相似,为了这一目标,我们继续前进吧!
允许C++赋予运算符多种含义,根据操作数的类型和个数决定所调用的函数。比如*,即可表示乘法,也可取相应地址的内容。
运算符函数:注意的是op必须是C++所定义的运算符,第一个操作数调用运算符函数,第二个操作数作为运算符函数的参数。
根据操作数据的个数和类型,调用合适的运算符函数
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
821.cpp class Time { int hours; int minuates; public: Time(int h,int m); Time(); Time operator+(const Time &) const; Time operator-(const Time &) const; Time operator*(double) const; void show() const; }; 2.cpp #include"1.h" #include<iostream> Time::Time() { hours = 0; minuates = 0; } Time::Time(int h , int m ) { hours = h; minuates = m; } Time Time::operator+(const Time & a) const { Time temp; int totalm = a.minuates + minuates; temp.minuates=totalm % 60; temp.hours = a.hours + hours + totalm / 60; return temp;//temp 拷贝到指定的内存或是寄存器,调用程序可访问到的位置 当函数执行完temp还是会消失的 不可以使用引用的原因在此 } Time Time::operator-(const Time &a) const { Time t; int ta = hours * 60 + minuates; int tb = a.hours * 60 + a.minuates; int temp = tb - ta; t.minuates = temp % 60; t.hours = temp / 60; return t; } Time Time::operator*(double x) const { Time t; int result = (hours * 60 + minuates)*x; t.hours=result / 60; t.minuates = result % 60; return t; } void Time::show() const { std::cout << hours << ":" << minuates << std::endl; } 3.cpp #include "stdafx.h" #include"1.h" #include<iostream> int main() { Time t1(1, 12); Time t2{ 2,33 }; Time t3 = t1 + t2; t3.show(); t3 = t1 - t2; t3.show(); t3=t2*1.5; t3.show(); std::cin.get(); return 0; }
重载运算符限制:
1.至少有一个参数是用户定义类型
2.不违反原来的句法(操作数个数看)
3.
4.可重载运算符
友元函数:
是对接口的扩充,提供了另一种形式的访问权限。
友元有三种:友元类、友元函数、友元成员函数
这里先讨论友元函数。
为何需要友元函数?
重载运算符时,操作数的顺序有限制,第一个操作数必须是对象,因而相同的运算符,却只支持一种表达式,另一中将产生编译错误。为了去除这种限制,只能使用非成员函数重载运算符,但是非成员函数却无法直接访问数据成员,这边产生了友元函数。
如何创建友元函数
将函数原型放在类声明中并以friend修饰。(在类中却不是成员函数,但又可以访问私有成员)
类型转化:
基本类型可以作自动类型转化是因为编译器得知了待转换的数据的类型是兼容的(数据含义相同)。
具体的存储大小不同,但都是数值
地址也是整数,但地址和数值含义不同,无法做自动类型转化,但可以进行强制类型转化。
自定义的类可能与基本类型紧密相连(一个类可能有多种表示,其中一种是用一个整数可表示的,可以理解整数或是类都是代表同一个概念)、或是与另一个类有相似的含义,因而实现自定义的类的类型转换至关重要。
类型不一样,编译器尝试进行类型转化(目的转为被赋值对象的类型),这一过程由构造函数实现,查看该类型是否含有赋值类型的参数的构造函数(没有完全相同类型的参数列表,则查找可兼容的参数列表,但要是出现多个可调用的构造函数,编译器会报错),若能找到唯一一个合适的构造函数则调用构造函数生成临时对象,再解释赋值运算符(逐成员赋值),隐式过程。
如果不想支持隐式转化,则可以在声明构造函数时加explicit,防止类型转化时的隐式调用构造函数。但如果希望由一个数值得到一个类,可进行强制类型转化,支持两张写法:类型()或是(类型) ,明显可以看出从一个整数到类对象就是靠构造函数。
不仅仅在赋值发生隐式(显式)类型转化,也可以在初始化、传参、返回值这些时刻发生类型转化。
举个小例子,为什么可以将一个字符串字面值直接赋值给String对象?因为String类中包含参数是char*的构造函数,因而可以创建String对象。
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88#ifndef STONE_H #define STONE_H #include<iostream> namespace STONE { class Stone { //enum{Lbs_to_stn=14}; static const int Lbs_to_stn = 14; double pounds; int stone; double pfs_left; public: Stone(); Stone(int, double); Stone(double); friend std::ostream & operator<<(std::ostream &, const Stone &); }; } #endif using namespace STONE; namespace STONE { Stone::Stone() { stone = pfs_left = pounds = 0; } Stone::Stone(int m, double n) { stone = m; pfs_left = n; pounds = m*Lbs_to_stn + pfs_left; } Stone::Stone(double m) { pounds = m; pfs_left =int (m )% Lbs_to_stn; stone = m/Lbs_to_stn; } std::ostream & operator<<(std::ostream & os, const Stone & ob) { using std::cout; using std::endl; cout << "仅用一个数值来表示:" << endl; os << ob.pounds << endl; cout << "另一种表示方法:" << endl; os << ob.pfs_left << " " << ob.stone << endl; return os; } } #include"stone.h" using STONE::Stone; void display(Stone m, int n); int main() { using std::cout; Stone s1 = 1222;//先变为double 再变为Stone 自动类型转化 编译器尽最大可能来保持程序的运行(多次转换) Stone s2(1, 1.3); Stone s3(33.222); Stone s4 = Stone(222); cout << s1; display(33.22, 2);//隐式类型转化 临时对象 std::cin.get(); return 0; } void display(Stone m, int n) { std::cout << m;//cout 和 Stone 对象 查找运算符函数 是这两个参数类型的 友元函数不是类内部函数 }
转化函数(类特殊成员函数):
用户为自定义类型定义的强制类型转化函数,用于将类转化为另一已知的类型。
编译器可通过待转化的类型到所在类中查找是否存在相应类型的转化函数,若有则调用该函数进行类型转化(隐式过程)
转化函数:
语法:operator typename()
1.必须是类方法且无参数,必须由对象调用,因而已经有了待转化的对象,因而语法设定没有参数
2.不能指定返回类型,由于typename已经表示了返回类型,但有返回值
避免隐式调用转化函数,explicit声明转化函数。
1
2
3
4
5
6
7explicit operator double(); Stone::operator double() { return pounds; } int x = int(s4);//由于使用了explicit使得 编译器不会进行自动类型转化,必须明确指出待转换类型
在实现运算符重载时,我们通常可采取成员函数或是友元函数,但成员函数只能通过对象调用,基本类型只能根据参数类型、返回类型、赋值类型来作自动转化,而不会平白无故的转为一个类型再去调用重载函数。因而无法处理运算符地一个操作数是基本类型,但是友元函数却不同,操作数根据友元函数参数类型可进行转化,定义友元函数的运算符重载适用范围更广。所以能用友元就用友元!
希望实现用户定义类型与基本类型的运算,可为具体的类型重载运算符,或是考虑编写的构造函数能否支持类型的自动转化,以类和类的方式处理。
关于自动转化函数,为了安全起见,如果需要,声明时一定用explicit修饰,防止编译器自动进行类型转化发生我们预料之外的情况。真真的需要转化了,就显示的写明要转化的类型(无论是将基本类型转为自定义类还是自定义类转为基本类型)
最后
以上就是洁净流沙最近收集整理的关于类特性的全部内容,更多相关类特性内容请搜索靠谱客的其他文章。
发表评论 取消回复