zoukankan      html  css  js  c++  java
  • 侯捷《C++面向对象开发》——动手实现自己的复数类

    前言

      最近在看侯捷的一套课程《C++面向对象开发》,刚看完第一节introduction之后就被疯狂圈粉。感觉侯捷所提及所重视的部分也正是我一知半解的知识盲区,我之前也写过一些C++面向对象的程序,不过正如侯捷所说,我还仅仅停留于Object-based层面,写程序时总是在想如何封装好一个类,而不是Object-oriented强调类与类之间关系的设计。

      这门课程分为两部分,第一部分讲Object-based,第二部分讲Object-oriented;第一部分又分为两部分:带指针的类的封装和不带指针类的封装。

      本文将以模板库中的complx复数类的部分内容为核心,在分析源代码的同时,讲解一些良好的代码风格和编程习惯,比如inline内联函数的使用、friend友元函数的使用、函数参数及返回值何时pass by value何时pass by reference等等。

    部分代码

    complex.h

     1 #ifndef __COMPLEX__
     2 #define __COMPLEX__
     3 
     4 class complex
     5 {
     6     public:
     7         complex(double r = 0, double i = 0)
     8             : re (r), im (i)
     9         { }
    10         complex& operator += (const complex&);
    11         double real () const { return re; }
    12         double imag () const { return im; }
    13     private:
    14         double re, im;
    15 
    16         friend complex& __doapl (complex*, const complex&);
    17 };
    18 
    19 #endif

    complex.cpp

     1 #include "complex.h"
     2 #include <iostream>
     3 
     4 using namespace std;
     5 
     6 inline complex& __doapl(complex* ths, const complex& r)
     7 {
     8     ths->re += r.re;
     9     ths->im += r.im;
    10     return *ths;
    11 }
    12 
    13 inline complex& complex::operator += (const complex& r)
    14 {
    15     return __doapl (this, r);
    16 }
    17 
    18 inline double imag (const complex& x)
    19 {
    20   return x.imag ();
    21 }
    22 
    23 inline double real (const complex& x)
    24 {
    25   return x.real ();
    26 }
    27 
    28 inline complex operator + (const complex& x, const complex& y)
    29 {
    30     return complex (real (x) + real (y), imag (x) + imag (y));
    31 }
    32 
    33 inline complex operator + (const complex& x, double y)
    34 {
    35     return complex (real (x) + y, imag (x));
    36 }
    37 
    38 inline complex operator + (double x, const complex& y)
    39 {
    40     return complex (x + real (y), imag (y));
    41 }
    42 
    43 ostream& operator << (ostream& os, const complex& x)
    44 {
    45     return os << ' (' << real (x) << "," << imag (x) << ')';
    46 }

    源码解析

    一、complex.h

    1.1 initialization list

             //程序1.1 
             complex(double r = 0, double i = 0)
                 : re (r), im (i)
             { }
    

      构造函数参数缺省,比较常规。

      值得注意的是,变量的初始化尽量放在初始化列表中(initialization list)。当然,完全可以在构造函数的函数体中赋值进行初始化。不过,侯捷指出,一个对象在产生过程中分为初始化和成功产生两部分,initialization list相当于在初始化过程中对变量赋值,而在函数体中赋值则是放弃了initialization list初始化这一过程,会降低效率。对于“性能榨汁机”的C++语言来讲,重视每个细节效率的重要性是毫无疑问的。

    1.2参数及返回值传递方式

            //程序1.2 2  
            complex& operator += (const complex&);     
    

      传递参数时,如果能用引用传递那么一定不要用值传递,因为值传递的过程中变量需要copy一份样本传入函数中,当参数很多或参数类型复杂时,会导致效率变慢。

      其次,如果函数不会改变参数的值,一定要加const限定,在初学时养成良好的变成习惯尤为重要。

      关于函数的返回值,同样是最好按引用传递,当然,有些情况无法按引用传递,这点将在2.3讲解。

      其实,参数列表中还隐藏一个this,这点将在2.2讲解。

    1.3友元函数

           //程序1.3
           friend complex& __doapl (complex*, const complex&);
    

      我们可以看到,在complex.h文件的末尾定义了一个友元函数,友元函数打破了类的封装,它不是类的成员函数,却可以使用点操作符来获取类的private变量。当然,非友元函数也可以通过get函数来获取,不过速度会慢一些。

    二、complex.cpp

    2.1 友元函数及内联函数

    //程序2.1
    inline complex& __doapl(complex* ths, const complex& r)
    {
        ths->re += r.re;
        ths->im += r.im;
        return *ths;
    }
    

      我们首先来分析一下这个友元函数,这里有两点值得探讨:

      第一这个函数将r的实部和虚部加到ths上,r在函数体中值没用发生改变,所以使用const限定。

      第二这个函数被设计成inline内联函数,我们都知道,内联函数是把代码块直接复制到函数需要调用的地方,通过省略函数调用这一过程来提高效率,那么我们为什么不将所有函数都设计成内联函数呢?其实我们的inline声明只是对编译器的一个建议,对于过于复杂的函数来讲,及时我们声明了inline,编译器也会调用执行。所以,对于一些“小巧”的函数,我们尽量设计为内联函数。

    2.2 隐藏的“this”

    //程序2.2
    inline complex& complex::operator += (const complex& r)
    {
        return __doapl (this, r);
    }
    

      操作符重载作为C++的特点之一,有令别的语言羡慕之处,当然也有些难以理解。

      实际上,这个函数的参数还有一个隐藏的this,这个this就是函数调用者

     

    2.3 不能为reference的返回值

    //程序2.3
    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));
    }
    

      注意,这里函数的返回值不能返回reference,这其实是使用临时对象(typename ()),在函数体内定义变量,然后把这个变量的引用传递出去,函数结束后变量本体死亡,传出去的引用既没有意义了。

    2.4 非成员函数的操作符重载

    //程序2.4
    ostream& operator << (ostream& os, const complex& x)
    {
        return os << ' (' << real (x) << "," << imag (x) << ')';
    }
    

     

      下面讲一下为什么有的操作符重载函数定义成非成员函数

      我们知道,操作符重载只作用在左边的操作数上,试想一下,如果把“<<”定义为成员函数,那每次调用岂不是要这样c1 << cout

      

    完整代码

      以上就是我在学习过程中特别注意的地方,下面给出complex类完整代码,只不过多了几种操作运算,大体思路完全一致。

    complex.h

      1 #ifndef __MYCOMPLEX__
      2 #define __MYCOMPLEX__
      3 
      4 class complex; 
      5 complex&
      6   __doapl (complex* ths, const complex& r);
      7 complex&
      8   __doami (complex* ths, const complex& r);
      9 complex&
     10   __doaml (complex* ths, const complex& r);
     11 
     12 
     13 class complex
     14 {
     15 public:
     16   complex (double r = 0, double i = 0): re (r), im (i) { }
     17   complex& operator += (const complex&);
     18   complex& operator -= (const complex&);
     19   complex& operator *= (const complex&);
     20   complex& operator /= (const complex&);
     21   double real () const { return re; }
     22   double imag () const { return im; }
     23 private:
     24   double re, im;
     25 
     26   friend complex& __doapl (complex *, const complex&);
     27   friend complex& __doami (complex *, const complex&);
     28   friend complex& __doaml (complex *, const complex&);
     29 };
     30 
     31 
     32 inline complex&
     33 __doapl (complex* ths, const complex& r)
     34 {
     35   ths->re += r.re;
     36   ths->im += r.im;
     37   return *ths;
     38 }
     39  
     40 inline complex&
     41 complex::operator += (const complex& r)
     42 {
     43   return __doapl (this, r);
     44 }
     45 
     46 inline complex&
     47 __doami (complex* ths, const complex& r)
     48 {
     49   ths->re -= r.re;
     50   ths->im -= r.im;
     51   return *ths;
     52 }
     53  
     54 inline complex&
     55 complex::operator -= (const complex& r)
     56 {
     57   return __doami (this, r);
     58 }
     59  
     60 inline complex&
     61 __doaml (complex* ths, const complex& r)
     62 {
     63   double f = ths->re * r.re - ths->im * r.im;
     64   ths->im = ths->re * r.im + ths->im * r.re;
     65   ths->re = f;
     66   return *ths;
     67 }
     68 
     69 inline complex&
     70 complex::operator *= (const complex& r)
     71 {
     72   return __doaml (this, r);
     73 }
     74  
     75 inline double
     76 imag (const complex& x)
     77 {
     78   return x.imag ();
     79 }
     80 
     81 inline double
     82 real (const complex& x)
     83 {
     84   return x.real ();
     85 }
     86 
     87 inline complex
     88 operator + (const complex& x, const complex& y)
     89 {
     90   return complex (real (x) + real (y), imag (x) + imag (y));
     91 }
     92 
     93 inline complex
     94 operator + (const complex& x, double y)
     95 {
     96   return complex (real (x) + y, imag (x));
     97 }
     98 
     99 inline complex
    100 operator + (double x, const complex& y)
    101 {
    102   return complex (x + real (y), imag (y));
    103 }
    104 
    105 inline complex
    106 operator - (const complex& x, const complex& y)
    107 {
    108   return complex (real (x) - real (y), imag (x) - imag (y));
    109 }
    110 
    111 inline complex
    112 operator - (const complex& x, double y)
    113 {
    114   return complex (real (x) - y, imag (x));
    115 }
    116 
    117 inline complex
    118 operator - (double x, const complex& y)
    119 {
    120   return complex (x - real (y), - imag (y));
    121 }
    122 
    123 inline complex
    124 operator * (const complex& x, const complex& y)
    125 {
    126   return complex (real (x) * real (y) - imag (x) * imag (y),
    127                real (x) * imag (y) + imag (x) * real (y));
    128 }
    129 
    130 inline complex
    131 operator * (const complex& x, double y)
    132 {
    133   return complex (real (x) * y, imag (x) * y);
    134 }
    135 
    136 inline complex
    137 operator * (double x, const complex& y)
    138 {
    139   return complex (x * real (y), x * imag (y));
    140 }
    141 
    142 complex
    143 operator / (const complex& x, double y)
    144 {
    145   return complex (real (x) / y, imag (x) / y);
    146 }
    147 
    148 inline complex
    149 operator + (const complex& x)
    150 {
    151   return x;
    152 }
    153 
    154 inline complex
    155 operator - (const complex& x)
    156 {
    157   return complex (-real (x), -imag (x));
    158 }
    159 
    160 inline bool
    161 operator == (const complex& x, const complex& y)
    162 {
    163   return real (x) == real (y) && imag (x) == imag (y);
    164 }
    165 
    166 inline bool
    167 operator == (const complex& x, double y)
    168 {
    169   return real (x) == y && imag (x) == 0;
    170 }
    171 
    172 inline bool
    173 operator == (double x, const complex& y)
    174 {
    175   return x == real (y) && imag (y) == 0;
    176 }
    177 
    178 inline bool
    179 operator != (const complex& x, const complex& y)
    180 {
    181   return real (x) != real (y) || imag (x) != imag (y);
    182 }
    183 
    184 inline bool
    185 operator != (const complex& x, double y)
    186 {
    187   return real (x) != y || imag (x) != 0;
    188 }
    189 
    190 inline bool
    191 operator != (double x, const complex& y)
    192 {
    193   return x != real (y) || imag (y) != 0;
    194 }
    195 
    196 #include <cmath>
    197 
    198 inline complex
    199 polar (double r, double t)
    200 {
    201   return complex (r * cos (t), r * sin (t));
    202 }
    203 
    204 inline complex
    205 conj (const complex& x) 
    206 {
    207   return complex (real (x), -imag (x));
    208 }
    209 
    210 inline double
    211 norm (const complex& x)
    212 {
    213   return real (x) * real (x) + imag (x) * imag (x);
    214 }
    215 
    216 ostream&
    217 operator << (ostream& os, const complex& x)
    218 {
    219   return os << '(' << real (x) << ',' << imag (x) << ')';
    220 }
    221 
    222 #endif   //__MYCOMPLEX__

    complex_test.cpp

     1 #include <iostream>
     2 #include "complex.h"
     3 
     4 using namespace std;
     5 
     6 int main()
     7 {
     8   complex c1(2, 1);
     9   complex c2(4, 0);
    10 
    11   cout << c1 << endl;
    12   cout << c2 << endl;
    13   
    14   cout << c1+c2 << endl;
    15   cout << c1-c2 << endl;
    16   cout << c1*c2 << endl;
    17   cout << c1 / 2 << endl;
    18   
    19   cout << conj(c1) << endl;
    20   cout << norm(c1) << endl;
    21   cout << polar(10,4) << endl;
    22   
    23   cout << (c1 += c2) << endl;
    24   
    25   cout << (c1 == c2) << endl;
    26   cout << (c1 != c2) << endl;
    27   cout << +c2 << endl;
    28   cout << -c2 << endl;
    29   
    30   cout << (c2 - 2) << endl;
    31   cout << (5 + c2) << endl;
    32   
    33   return 0;
    34 }

    总结

      作为初学者,一定要养成良好的编程习惯,正如侯捷所说:“一出手就是大家风范”。

  • 相关阅读:
    nodejs学习笔记
    javascript 中常用的判断和循环方法
    javascript中几种数据类型。
    通过Jquery找对象的方法
    js 原生代码找对象的方法
    网页设计时的渐进增强和优雅降级原则
    require.js笔记
    This in JavaScript
    [转] java.lang.IllegalArgumentException: Document base D:apache-tomcat-7.0.47webappsXXX错误
    后台获取前台表单数据
  • 原文地址:https://www.cnblogs.com/henuzyx/p/9107842.html
Copyright © 2011-2022 走看看