Boolan第一周笔记(一)复合赋值运算符和算术运算符究竟该重载成成员函数仍是非成员函数

第一周侯捷老师以complex类为例讲解了不含指针的类的写法。讨论群里有一位同窗提出了一个问题,大意是:对于侯老师给出的complex类(下面是部分代码),为何+=设计为成员函数就是合理的,+设计为成员函数就是不合理的。我经过翻书和写代码获得的结论是:对于左数是complex类的对象的状况,+= 和 +做为成员函数或非成员函数都不影响对它们的调用,程序均可以正确运行,可是+=设计成成员函数,+设计成非成员函数更合理。缘由是:在为自定义的类定义运算符时要尽可能模仿内置类型的写法,这样作符合使用咱们自定义类型的用户长期以来的习惯(用户怎样使用内置类型的运算符,如今就能够以一样的方式使用咱们自定义的运算符)。这一点侯捷老师还有C++ Primer都提到过。ios

本文的讨论过程和结论并不保证彻底正确(但代码确定对),它只是我对这个问题作的探讨和尝试,欢迎有更好想法的朋友与我交流。面试

如下是跟这位同窗的疑问有关的代码,来自侯捷老师 & Boolan C++工程师微专业。函数

#ifndef __MYCOMPLEX__ #define __MYCOMPLEX__

class complex; complex& __doapl(complex* ths, const complex& r); class complex { public: complex(double r = 0, double i = 0) : re(r), im(i) { } complex& operator += (const complex&); double real() const { return re; } double imag() const { return im; } private: double re, im; friend complex& __doapl(complex *, const complex&); }; inline complex& __doapl(complex* ths, const complex& r) { ths->re += r.re; ths->im += r.im; return *ths; } inline complex& complex::operator += (const complex& r) { return __doapl(this, r); } inline double imag(const complex& x) { return x.imag(); } inline double real(const complex& x) { return x.real(); } inline complex operator + (const complex& x, const complex& y) { return complex(real(x) + real(y), imag(x) + imag(y)); } inline complex operator + (const complex& x, double y) { return complex(real(x) + y, imag(x)); } inline complex operator + (double x, const complex& y) { return complex(x + real(y), imag(y)); } #endif // !__MYCOMPLEX__

先回答+=的设计。this

首先,+=并非非得写成成员函数。C++ Primer(中文第五版,第500页)有提到“复合赋值运算符并不非得是类的成员,不过咱们仍是倾向于把包括符复合赋值运算符在内的全部赋值运算都定义在类的内部“。就是说,把复合赋值运算符定义在类内是推荐使用的作法,可是并不严格要求必须这样作。这一点经过如下的程序能够获得证实。spa

1.复合赋值运算符做为类的成员函数:设计

//complex.h
#ifndef __MYCOMPLEX__ #define __MYCOMPLEX__ #include<iostream>
using namespace std; class complex; complex& __doapl(complex* ths, const complex& r); class complex { public: complex(double r = 0, double i = 0) : re(r), im(i) { } complex& operator += (const complex&); double real() const { return re; } double imag() const { return im; } private: double re, im; friend complex& __doapl(complex *, const complex&); }; inline complex& __doapl(complex* ths, const complex& r) { ths->re += r.re; ths->im += r.im; return *ths; } inline complex& complex::operator += (const complex& r) { cout << "complex& complex::operator += (const complex& r) " << endl; return __doapl(this, r); } #endif   //__MYCOMPLEX__
//complex_test.cpp
#include "complex.h" ostream& operator << (ostream& os, const complex& x) { return os << '(' << x.real() << ',' << x.imag() << ')'; } int main() { complex c1(2, 1); complex c2(4, 0); cout << "c1 + c2 =" << (c1 += c2) << endl; system("pause"); return 0; }

运行结果指针

 

2.符合赋值运算符不是类的成员
code

//complex.h
#ifndef __MYCOMPLEX__ #define __MYCOMPLEX__ #include <iostream>
using namespace std; class complex; complex& __doapl(complex& ths, const complex& r); class complex { public: complex(double r = 0, double i = 0) : re(r), im(i) { } double real() const { return re; } double imag() const { return im; } private: double re, im; friend complex& __doapl(complex&, const complex&); }; inline complex& __doapl(complex& l, const complex& r) { l.re += r.re; l.im += r.im; return l; } inline complex&
operator += (complex& l, const complex& r) { cout << "complex& operator += (complex& l, const complex& r)"; return __doapl(l, r); } #endif   //__MYCOMPLEX__
//complex_test.cpp
#include "complex.h" ostream& operator << (ostream& os, const complex& x) { return os << '(' << x.real() << ',' << x.imag() << ')'; } int main() { complex c1(1,2),c2(2, 1); cout << (c1+=c2) << endl; system("pause"); return 0; }

运行结果对象

观察运行结果可知,+=是否做为类的成员函数都不影响对+=的调用。可是+=做为一种赋值运算符,最好使跟内置的赋值运算符同样写成成员函数。
blog

接下来讨论对于开头的第一段代码,+运算符什么状况下须要写成成员函数,什么状况下须要写成非成员函数。

这段代码重载了三个+运算符,首先讨论下面这个。

inline complex operator + (double x, const complex& y) { return complex (x + real (y), imag (y)); }

C++ Primer(中文第五版 493页)提到,“当咱们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象”。这个+的左侧是double,所以不能够写成complex类的成员函数。另外,double类型是内置类型,没有定义一个double 和一个复数的+运算符。因此咱们须要本身写这个+,并把它写成complex的非成员函数。

下面的两个+运算符做为complex类的成员函数或非成员函数,程序均可以运行。

inline complex operator + (const complex& x, const complex& y) { return complex (real (x) + real (y), imag (x) + imag (y)); } inline complex operator + (const complex& x, double y) { return complex (real (x) + y, imag (x)); }

开头第一段代码就是把这两个+做为非成员函数,程序能够运行,没有错误。下面试试成员函数版本。

//complex.h
#ifndef __MYCOMPLEX__ #define __MYCOMPLEX__ #include <iostream>
using namespace std; class complex { public: complex(double r = 0, double i = 0) : re(r), im(i) { } double real() const { return re; } double imag() const { return im; } complex operator + (const complex&); complex operator + (double); private: double re, im; }; inline complex complex::operator + (const complex& y) { cout << "complex complex::operator + (const complex& y)" << endl; return complex(real() + y.real(), imag() + y.imag()); } inline complex complex::operator + (double y) { cout << "complex complex::operator + (double y)" << endl; return complex(real() + y, imag()); } #endif   //__MYCOMPLEX__
//complex_test.cpp
#include "complex.h" ostream& operator << (ostream& os, const complex& x) { return os << '(' << x.real() << ',' << x.imag() << ')'; } int main() { complex c1(2, 1); complex c2(4, 0); cout << (c1 + c2) << endl; cout << (c1 + 5) << endl; system("pause"); return 0; }

运行结果

把这两个+做为成员函数,程序也能够运行,没有错误。

那为何+写成非成员函数更好呢?

C++ Primer(中文第五版 第493页)在谈到把重载运算符做为成员函数或非成员函数时,提到“具备对称性的运算符可能转换任意一端的运算对象,例如算术,相等性,关系和位运算符等,所以它们一般应该是普通的非成员函数”。接下来书中提到了string的 operator+的例子来讲明这一点。string 的+重载成了非成员函数正是因为对称型运算符可能转换任意一端的对象。

string s = "world"; string t = s + "!"; string u = "hi" + s;

string 的+是链接两个string对象的,就是说左侧和右侧的对象都是string。所以不管+是重载成成员函数仍是非成员函数,第二行的表达式 s+"!"都是对的(由于左侧是string, 右侧的const char *能够转换成string)。可是对于 “hi" + s 就不同了,若是string 的+是成员函数, 就会严格要求+的左侧对象是string,那么这个表达式在运行时就找不到匹配的运算符。所以,显然的,+运算符做为非成员函数比做为成员函数更灵活,所以更好。

在本周课程的complex类里,左数是complex类型的两个+运算符写成非成员函数比写成成员函数更好,我推测正是由于对称性的运算符可能转换运算对象(也许是有其余的缘由,可是我目前不知道,之后发现了再修改)。好比当用户想要计算c1(1, 0)和 c2 (2, 1)的和,但忘记输入c1的虚部时,就至关于输入了一个int和一个c2。 若是complex类把两个complex对象相加写成成员函数,int能够转换为double, double则能够转换成complex,这时c1 + c2就能够正常计算,就像上一段中"hi"和s相加发生的转换过程相似。

下面的代码能够验证当complex类把两个complex对象相加使用的+运算符写成非成员函数时,+左侧若是输入int, 该int可转换为complex。

//complex.h
#ifndef __MYCOMPLEX__ #define __MYCOMPLEX__ #include <iostream>
using namespace std; class complex { public: complex(double r = 0, double i = 0) : re(r), im(i) { } double real() const { return re; } double imag() const { return im; } private: double re, im; }; inline double imag(const complex& x) { return x.imag(); } inline double real(const complex& x) { return x.real(); } inline complex operator + (const complex& x, const complex& y) { cout << "complex operator + (const complex& x, const complex& y)" << endl; return complex(real(x) + real(y), imag(x) + imag(y)); } #endif   //__MYCOMPLEX__
//complex_test.cpp
#include "complex.h" ostream& operator << (ostream& os, const complex& x) { return os << '(' << x.real() << ',' << x.imag() << ')'; } int main() { int i = 1; complex c2(2, 1); cout << i + c2 << endl; system("pause"); return 0; }

运行结果

计算int + complex调用了complex + complex的函数,说明存在从int到complex的隐式转换。若是把+写成complex的成员函数,编译没法经过,计算 i+ c2(模仿忘记输入左数的虚部的状况)的时候会显示没有匹配的运算符。

相关文章
相关标签/搜索