zoukankan      html  css  js  c++  java
  • C++面向对象高级开发课程(第一周)

    0. 内存分区

      计算机中的内存在用于编程时,被人为的进行了分区(Segment),分为:

      -“栈区”(Stack)

      -“堆区”(Heap)

      -全局区(静态 区,Static)

      -文字常量区和程序代码区

      在前面的课程中,我们主要直接涉及到的是栈区的内存,在你的程序中,函数的参数值,局部变量的值等都被存在 了“栈区”,这部分的内存,是由系统来帮助你来管理的,没有特殊情况的时候,你是不需要对其进行特别处理的。计算机中内存的分配如下图。

      

      而针对堆区的内存,一般由程序员进行分配和释放, 使用堆内存的原因一般是

      -“栈上内存比较小,不够用”

      -“系统管理内存的方式死板,不方便用”

      对于堆上的内存,被程序员手动分配后,若程序员 不释放就可能会出现“内存泄漏”。很多企业级的应用,都因为内存泄漏而在“正常”运转很长时间后,轰然“坍塌”。在后面的入门课程中,我们会简单的对这块 的知识进行介绍。

      全局区、文字常量区和程序代码区在我们入门阶段,暂时还可以不去过多理解(甚至看不懂也无妨),只需要知道他们的大致作用即可——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放;文字常量区是用于储存常量字符串的, 程序结束后由系统释放;程序代码区用于存放函数体的二进制代码。

    1. C++类的写法

    C++类的两大经典写法:

    Class without pointer member(s)  比如 课程中定义的 Complex 类。

    and  

    Class with pointer member(s)  比如 系统中的 string 类。

    2. C++正规、大气的写法

    2.1 使用“防卫式声明”

    #ifndef _COMPLEX_
    #define _COMPLEX_
    
    class Complex
    {
      //some declaration
    }
    
    #endif

    2.2 使用“构造函数初始值列表”

    尽量在声明构造函数时使用“构造函数初始值列表”,因为这样在变量的声明阶段就赋予了值,有助于提高效率。

    2.3 使用“const”

    为了代码在以后被其他程序员复用中,在“顶层”能使用const关键字限定行为,所以我们在做“底层”工作时,要充分考虑到使用const限定函数行为。

    同时注意C++语法的“顶层const”原则。

    另外,在类body的定义中,也尽量使用“const”语法。

    2.4 使用”引用“

    尽量考虑使用“引用”这一语法。因为在传递的过程中开销比较小—— 一个4字节的指针。

    但是引用也有一些缺点,比如引用的不安全性——引用的母体的值可能会通过子体被修改。所以我们可以在引用的地方考虑使用const。

    3. inline

    函数若在 class body 内完成定义,该函数便自动成为 inline “候选人”。

    太复杂的函数无法成为“真正的” inline function,即便是用 inline 声明。

    4. 参数传递

    两种方式:

    pass by value  程序栈内数据直接拷贝,开销比较大。

    and  

    pass by reference (to const)  “引用”语法的底层实现是指针,所以传4字节的指针开销比较小。形式美观。

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

      

    6. 友元

    6.1 提高性能

    在一些情况下,为了提高性能,请尽量通过友元实现数据存取。

    因为通过使用友元语法,可以省略掉一步多余的——通过类共有方法实现存取类私有数据——操作,因此可以提高性能。但也破坏了封装。

    6.2 特性

    同一类的各个对象互为友元。例如下面代码:

    class complex
    {
    public:
        Complex (double r = 0, double i = 0)
            :   re(r), im(i)
        {   }
        
        int func(const complex& param)
        {   return param.re + param.im;  }
        
    private:
        double re, im;
    };
    
    int main()
    {
        complex c1(2,1 );
        complex c2();
        
        c2.func(c1);
    }

    7. “引用”的不适用场景

    inline complex&
    __doapl (complex* ths, const complex& r)
    {
      ths->re += r.re;    //第一参数将会被改动
      ths->im += r.im;    //第二参数将不会被改动
      return *ths;
    }

    因为 r 是一个引用,当函数执行完成,空间被回收以后,引用这一实体将不存在。

    8. 如何实现C++高性能

    侯杰认为,好的C++程序应该具备

    • 数据尽可能放在 private 区 
    • 参数尽可能使用 reference 传递
    • 返回值尽可能使用 reference 传递
    • class body 中尽可能使用 const 语法
    • 尽可能使用 构造函数初始值列表

    9. 运算符重载

    9.1 成员函数

    作为类的成员函数,运算符重载要写成如下的形式。“+=”运算符右边的操作数作为函数的参数被传递;“+=”运算符左边的操作数作为类内部的 this 指针。

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

    为了便于理解,可以写成如下形式(语法错误,但是便于理解)。

    // 语法有错误,但是便于理解。注意第一个参数:this
    inline complex& complex::operator += (this, const complex& r)
    {
      return __doapl (this, r);
    }

     9.2 非成员函数

    作为非成员函数的重载,函数定义时operator关键字前面不应该有 “类名::”限定符。代码如下:

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

    其中,return complex(......)表示声明了一个临时对象,并将其直接返回。这样的写法——返回临时对象——在STL中很常见。

    如果作为“成员函数”的重载,代码如下:

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

    10. return by reference 语法分析

    传递者无需知道接收者是从reference形式接收。 

    11. STL :: complex类中的优良写法

    • initialization list (构造函数初始化列表)
    • 函数 const
    • 参数传递尽量 pass by reference
    • 函数返回值尽量 return by reference
    • 数据声明在 private 

    12. 关于引用的讨论

    引用传递的底层实现是一根4字节的指针,所以开销相比较而言较小。

    但是对于“基本内置类型”比如 bool, double, char[4]以内, int直接传递其本身与传递引用效率是一样的,这就需要程序员根据编程规范自己取舍。

    13. 成员函数声明时const

    在声明成员函数时加上const关键字,如代码所示:

    class complex
    {
    public:
      double real () const { return re; }
    };

    表示该函数不被允许修改类中的成员变量。

    14. C++组成部分

    C++由两部分组成:C++语言本身 和 C++标准库。

    15. STL complex类代码区域划分

    主要分成三部分:前置声明、类声明、类定义。

    #ifndef __COMPLEX__
    #define __COMPLEX__
    
    /* 0 前置声明 */
    #include <cmath>
    
    class ostream;
    class complex;
    
    complex& __doapl ( complex* ths, const complex& );
    /* end  0 前置声明 */
    
    /* 1 类声明 */
    class complex
    {
    
    };
    /* end 1 类声明 */
    
    /* 2 类定义 */
    complex::function....
    /* end 2 类定义 */
    
    #endif // __COMPLEX__

    16. 附录

    complex 类代码如下:

    #ifndef __MYCOMPLEX__
    #define __MYCOMPLEX__
    
    class complex; 
    complex&
      __doapl (complex* ths, const complex& r);
    complex&
      __doami (complex* ths, const complex& r);
    complex&
      __doaml (complex* ths, const complex& r);
    
    
    class complex
    {
    public:
      complex (double r = 0, double i = 0): re (r), im (i) { }
      complex& operator += (const complex&);
      complex& operator -= (const complex&);
      complex& operator *= (const complex&);
      complex& operator /= (const complex&);
      double real () const { return re; }
      double imag () const { return im; }
    private:
      double re, im;
    
      friend complex& __doapl (complex *, const complex&);
      friend complex& __doami (complex *, const complex&);
      friend complex& __doaml (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 complex&
    __doami (complex* ths, const complex& r)
    {
      ths->re -= r.re;
      ths->im -= r.im;
      return *ths;
    }
     
    inline complex&
    complex::operator -= (const complex& r)
    {
      return __doami (this, r);
    }
     
    inline complex&
    __doaml (complex* ths, const complex& r)
    {
      double f = ths->re * r.re - ths->im * r.im;
      ths->im = ths->re * r.im + ths->im * r.re;
      ths->re = f;
      return *ths;
    }
    
    inline complex&
    complex::operator *= (const complex& r)
    {
      return __doaml (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));
    }
    
    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));
    }
    
    inline complex
    operator * (const complex& x, const complex& y)
    {
      return complex (real (x) * real (y) - imag (x) * imag (y),
                   real (x) * imag (y) + imag (x) * real (y));
    }
    
    inline complex
    operator * (const complex& x, double y)
    {
      return complex (real (x) * y, imag (x) * y);
    }
    
    inline complex
    operator * (double x, const complex& y)
    {
      return complex (x * real (y), x * imag (y));
    }
    
    complex
    operator / (const complex& x, double y)
    {
      return complex (real (x) / y, imag (x) / y);
    }
    
    inline complex
    operator + (const complex& x)
    {
      return x;
    }
    
    inline complex
    operator - (const complex& x)
    {
      return complex (-real (x), -imag (x));
    }
    
    inline bool
    operator == (const complex& x, const complex& y)
    {
      return real (x) == real (y) && imag (x) == imag (y);
    }
    
    inline bool
    operator == (const complex& x, double y)
    {
      return real (x) == y && imag (x) == 0;
    }
    
    inline bool
    operator == (double x, const complex& y)
    {
      return x == real (y) && imag (y) == 0;
    }
    
    inline bool
    operator != (const complex& x, const complex& y)
    {
      return real (x) != real (y) || imag (x) != imag (y);
    }
    
    inline bool
    operator != (const complex& x, double y)
    {
      return real (x) != y || imag (x) != 0;
    }
    
    inline bool
    operator != (double x, const complex& y)
    {
      return x != real (y) || imag (y) != 0;
    }
    
    #include <cmath>
    
    inline complex
    polar (double r, double t)
    {
      return complex (r * cos (t), r * sin (t));
    }
    
    inline complex
    conj (const complex& x) 
    {
      return complex (real (x), -imag (x));
    }
    
    inline double
    norm (const complex& x)
    {
      return real (x) * real (x) + imag (x) * imag (x);
    }
    
    #endif   //__MYCOMPLEX__
  • 相关阅读:
    vuex 入门
    Vuex 原理
    java web开发问题集合
    JSP Servlet 路径解析 路径设置
    随笔——学习的一些步骤及注意点(不断更新)
    常用正则表达式大全!(例如:匹配中文、匹配html)
    myeclipse+tomcat 工程名改名的问题 ——————完美解决方案
    web开发的步骤
    Servlet中的GET和POST之间的区别
    html、css、javascript、JSP 、xml学习顺序应该是怎样的呢?
  • 原文地址:https://www.cnblogs.com/fengyubo/p/4775967.html
Copyright © 2011-2022 走看看