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__

 

posted @ 2015-09-01 17:21  健康平安快乐  阅读(509)  评论(0编辑  收藏  举报