运算符重载 Ⅱ
注意事项
函数对象
当定义了 operator()
的类的对象调用此操作符时,其表现形式如同普通函数调用一般,故取名函数对象,举例:
1 2 3 4 5 6
| class cmp { public: bool operator () (const int& a, const int& b) { return a < b; } };
|
1 2 3 4 5
| int main() { cmp f; cout << f(1,2) << endl; return (0); }
|
函数对象实际上也是一个对象,所以可以拥有自己的成员变量,从而执行带谓词的函数执行:
1 2 3 4 5 6 7 8 9 10 11 12
| class greaterThan { private: int base;
public: greaterThan(int x) : base(x) {} bool operator () (const int& x) { return x > base; } };
|
1 2 3 4 5 6
| #include "greater.h" int main() { greaterThan g(10); cout << g(15) << endl; return (0); }
|
所以,函数对象相比普通的函数有一个非常重要的用途,即 作为谓词函数(Predicate)。
谓词函数通常用来对传进来的参数进行判断,并返回布尔值。标准库中有大量的函数都定义了多个重载版本,其中包含由用户提供谓词函数的,比如:find_if,remove_if,等等。
现在假设我们有一串数字,要从中找出第一个大于10的数字:
1 2 3 4 5 6 7 8 9 10 11 12
| #include "greater.h" bool greaterThan10(const int& x) { return x > 10; } int main() { vector<int> a = {5, 10, 15, 20, 25}; cout << *find_if(a.begin(), a.end(), greaterThan(10)) << endl; cout << *find_if(a.begin(), a.end(), greaterThan10) << endl; return (0); }
|
友元函数
问题引入
Q:如何解决外部函数无法访问类的 private 成员?
1 2 3 4 5 6 7 8 9 10 11 12 13
| class integer { int x; };
istream& operator >> (istream& is, integer& Int) { is >> Int.x; return is; }
ostream& operator << (ostream& os, const integer& Int) { os << Int.x; return os; }
|
A:使用友元函数(friend function)
概念
friend return_type function_name(parameter_type_list);
将正常声明的函数放进类内部,并在前面加上 friend
关键字,那么这个函数虽然不属于类,但却可以访问类的私有变量以及私有函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class integer { int x; friend istream& operator >> (istream& is, integer& Int); friend ostream& operator << (ostream& os, const integer& Int); };
istream& operator >> (istream& is, integer& Int) { is >> Int.x; return is; }
ostream& operator << (ostream& os, const integer& Int) { os << Int.x; return os; }
|
友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。
类的成员函数也是一种函数,所以,其他类的成员函数也可以作为友元函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class integer;
struct cmp { bool operator () (const integer& a, const integer b); };
class integer { private: int x;
public: integer(int x = 0) : x(x) {} friend bool cmp::operator () (const integer& a, const integer& b); };
bool cmp::operator () (const integer& a, const integer& b) { return a.x < b.x; }
|
友元类
有时候其他类的成员函数可能会很多,一个一个的声明为友元函数会比较麻烦。
所以我们就可以直接声明友元类:
- 一个类 A 可以将另一个类 B 声明为自己的友元,那么类 B 的所有成员函数就都可以访问类 A 对象的私有成员
friend class B;
(在类 A 的内部)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class integer;
struct cmp { bool operator () (const integer& a, const integer b); };
class integer { private: int x;
public: integer(int x = 0) : x(x) {} friend cmp; };
bool cmp::operator () (const integer& a, const integer& b) { return a.x < b.x; }
|
操作符重载限定
必须是函数成员
隐式转换
问题引入
对象隐式转换
如果对象 T 存在构造函数 T(T1), 则 T1 类型对象(实参)可隐式转为 T 类型对象(形参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class integer { int x;
public: integer(int x = 0) : x(x) {} friend integer operator + (const integer& lhs, const integer& rhs) { return lhs.x + rhs.x; } friend ostream& operator << (ostream& o, const integer& hs) { o << hs.x; return o; } };
int main() { string s; s = "Hello"; cout << s << endl;
integer i1(3), i2; i2 = 1.1 + i1; cout << i2 << endl; return (0); }
|
重载协议-const
如果重载的函数参数一样,可以通过转换到某个重载函数,编译会如何哪个版本的选择?
- 类型直接匹配的优先选择;
- const 类型实参匹配 const 版本
- 非 const 实参优先匹配非 const 版本。没有则隐式转换为 const 版本匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Integer { int x;
public: integer(int x = 0) : x(x) {} friend ostream& operator << (ostream& o, const Integer& hs) { o << "const " << hs.x; return o; } friend ostream& operator << (ostream& o, Integer& hs) { o << "no_const " << hs.x ; return o; } };
int main() { Integer i1(1); const Integer i2(2); cout << i1 << "," << 3 << "," << i2 << endl; }
|
练习:
- 注释去除非 const 版本,编译运行
- 注释去除 const 版本,编译
nullptr
C 语言零值常数有很多表示,如 0, NULL, ‘\0’, C++ 右引入了 nullptr 表示空值指针字面量。
下边的例子解释了 nullptr 的必要性
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
| class Integer { int x; `public: Integer(int x = 0) : x(x) {} friend Integer operator + (const Integer& lhs, Integer rhs){ return lhs.x + rhs.x; } friend Integer operator+(const Integer& lhs, Integer* rhs){ if (rhs) { return lhs.x + rhs->x + 2000; } else { return lhs.x + 1000; } } friend ostream& operator << (ostream& o, const Integer& hs) { o << hs.x; return o; } };
class Girlfriend {};
void kissGirlfriend(Girlfriend* gf) { cout << "pointer"<<endl; }
void kissGirlfriend(int gfID) { cout << "int"<<endl; }
int main() { kissGirlfriend(nullptr); kissGirlfriend(0);
Integer i1(1), i2; i2 = i1 + 0; cout << i2 << endl; return (0); }
|
先进行类型匹配
再进行类型转换
0优先转指针了,不会执行integer类型转换
运算符重载,0必须特殊处理。如采用显式转换(integer)0转指针了,不会执行integer类型转换
explicit
C++ 关键字 explicit,用于关闭这种自动类型转化的特性。
即被 explicit 关键字修饰的类构造函数,不能进行自动地隐式类型转换,只能显式地进行类型转换。
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
| class Integer; struct cmp { bool operator () (const Integer& a, const Integer& b); };
class Integer { int x;
public: explicit Integer(cosnt char* s) : x(atoi(s)) {} Integer(int x = 0) : x(x) {} friend cmp; };
bool cmp::operator () (const Integer& a, const Integer& b) { return a.x < b.x; }
int main() { Integer a("3"), b("4"); cmp compare; cout << compare(a,b) << endl; cout << compare(Integer("1"), Integer("2")) << endl; cout << compare((Integer)"1", (Integer)"2") << endl;
cout << compare("1", "2") << endl; cout << compare("a", "b") << endl; return (0); }
|