这个是候捷版本的C++面向对象高级开发的课程,感觉不错,记下来
头文件的写法:(防卫式的声明)
头文件有一种非常正规的写法,一定要学习起来:
#ifndef _COMPLEX_ #define _COMPLEX_ . . . . . #endif
一个头文件先这么写(如上),为什么要写这个东西呢?因为有很多程序,要用到你这个头文件,或者你自己也可能用到,如果有很多头文件,用户在调用(include)的时候,会出现到底先调用那个头文件的问题,头文件很多的话,会很难排,所以,这种防卫式的声明,非常管用。大致原理是:但机器执行的时候,一读到第一行代码告诉编译器说:一进来,如果不曾经定义过“_COMPLEX_”这个东西,那么就把它定义出来,然后就继续执行下面的内容,所以,当程序第一此调用(include)这个头文件的话,很容易就被定义好了,在同一个函数里面,第二次再(include)的时候,因为“_COMPLEX_”已经被定义好了,就不会再进来了,下面的代码也就不会再被执行了,这样就可以避免头文件被重复引入。
Header(头文件)的布局:
那么下面就开始写中间的内容部分,先写第一部分:
class的声明(declaration):
任何一个class一定有一个“头”(class head),这里面你就需要去设计,我的复数应该具备什么样的数据,我的应该准备什么样的函数,才能够满足使用复数的人的需求,比如说,大家都知道数学里面有个共轭复数,那我就应该在这里写一个共轭复数这样的函数出来。那结果就是这样
class complex /*............................<-class head,“complex”也是类的名称*/ /*................................class body如下*/ { public: complex (double r = 0, double i = 0) : re (r), im (i) { } complex& opeartor += {const complex&}; double real () const { return re;} double imag () const { return im;} private: double re, im;/*复数里面有实部和虚部,那我就人为的来定义一下*/ friend complex& __doapl (complex*, const complex&) };
在函数当中的用法为:
{ complex c1(2,1); complex c2; . . . }
inline函数:最好将大部分函数都写成inline的形式,上例中“{ return re; }”“{ return im;}”构成了inline函数,函数若在class body内定义完成,便自动成为inline函数,如果在外头定义,就不是inline函数了。但是,要注意的是,如果函数太过于复杂,就没有办法inline了。比如说,就这两个函数,我们设计成inline函数,但具体是不是按inline来编译,那就是编译器的事情了。
如果是在非class body下定义完成的inline函数,举例如下:
inline double imag(const complex& x) { return x.imag () ; }
在一个class body中,很明显可以区分为几大段,用什么来区分呢?就是“public”(公开的,被外界所看的到的)和“private”(只有我class里面才可以看得到)。那什么东西才可以放到“private”里面呢?当然是数据的部分,因为我们需要的数据是封装起来的,不要被外界任意看到。那函数部分呢?函数也分两种,一种给外界用的,一种来处理自己私人的事情,在这个例子里面,大部分函数都是要被外界所使用的,所以,所有函数我们都放在"public"里面。这些段落是可以交错的,想到一段写“public”,再想到一段写“private”无先后顺序,或只能写两段。
那这有什么影响呢?
举个例子:
{ complex c1(2,1); cout << c1.re; cout << c1.im; }
这样写就是错的,因为“re”和“im”定义在“private”里面,是不会被读取出来的。
而这样写:
{ complex c1{2,1}; cout << c1.real(); cout << c1.imag(); }
是可以的,因为调用的是"public"里面的“real”和“imag”函数。
最后总结一下,我们把“public”“private”他们所形成的这个区域,称为:“access level ”访问级别。
如果你写的函数打算被外界调用,那你就放在“public”里面,如果你写的函数,只打算在内部被运用,没打算被外界调用,那你就放在“private”
在C++里面,如果你想创建一个东西(就是对象),有一个函数会自动被调用起来,不需要人工调用,自动起来。这就叫做构造函数,比如,下面这个例子:
{ complex c1(2,1); complex c2; complex * p = new complex(4); . . . }
这三个都在创建对象,创建对象的话,一定有一个函数在里面。
这种构造函数的写法很独特,它的函数名称,一定要跟类的函数名称相同,才能够叫做构造函数,并且,它可以用有参数,在class body 里面,complex头文件定义部分。自然想到实部和虚部两个部分来定义。
complex (double r = 0, double i = 0) : re (r), im (i)/*这行是初始化,只有构造函数才有,表示把r分给re,把i分给im*/ { }
其中“r=0”和“i=0”都是默认实参(default argument)。就像上面的那个c1有指明参数,那就是用它指明的,c2没有指明的参数,那就用class body 里面定义好的默认实参好了
充分利用每个函数的特点,是判断编写是否大气的标志,上面就是大气的一个典范!
构造函数可以有很多个-overloading(重载),比如说你设计一个类,等待外界来创建一个对象,创建对象的时候要怎么创建?假设你有三种设计思路,那你就写出三个构造函数,C里面是不允许这样做的,但C++是允许的。相同函数名称,却不止一个,这个就叫做overloading
举个例子:
void real(double r) ( re=r;)
这样定义也是可以的,虽然,会跟class body 里面的real冲突,它们机器码不一样。
但如果是这种情况下:
complex (double r = 0, double i = 0) : re (r), im (i) { } complex () : re(0) , im(0) {}
因为重名,会出现冲突。
函数重载,一般多发生在构造函数里面。
* class template(模板)
在上面的class中,实部和虚部是人工定义为double值,那我如果要设计另外一个复数,它里面的类型是浮点数(float),或者是整数,怎么办?可上面已经写死了,不能动了,那就只好再把原来的程序原样敲一遍,只到定义实部和虚部的部分,改一下,这样写太麻烦了,于是,C++的设计者,就发明了针对类似这种情况的东西————class template(模板),写成:把实部和虚部的位置不要先写死,我想要以后用的时候,我再指定
template<typename T>/*提前告诉编译器,T是一个type*/ class complex { public: complex (double r = 0, double i = 0) : re (r), im (i) { } complex& opeartor += {const complex&}; double real () const { retrun re; } double imag () const { return im;} private: T re, im;/*T的意思可以理解成告诉编译器:我现在是什么类型,我还没有决定呦!*/ friend complex& __doapl (complex*, const complex&) };
在函数当中的用法为:
{ complex<double> c1(2.5,1.5); complex<int> c2(2, 6); . . . }