1.头文件中的防卫式声明
例如自己要建复数complex类,可以建立一个complex.h的头文件。
头文件中必须先写防卫式声明:
#ifndef __COMPLEX__ #define __COMPLEX__ //此处为头文件的其他内容 //在2点会描述这个的布局 #endif
2.头文件布局
#ifndef __COMPLEX__ #define __COMPLEX__ //这个布局有3部分,按顺序是以2、3、1部分写 //1.前置声明,写完2、3部分后检测一下, //是不是有些东西需要在前面先声明过才可以 #include <iostream> #include <cmath> class ostream; class complex; complex& __doapl(complex* ths, const complex& r); //2.类的声明,写类该有的一些东西 class complex{ ... }; //3.类的定义,功能的具体实现 complex::function(){} ... #endif
3.在写第2部分时,率先考虑该类有声明数据,把数据放在private处;
然后写其函数,那实现想其构造函数,写构造函数时考虑其会有哪些参数,要不要默认值;
写构造函数参数时,需要考虑参数是用 &传递,还是value传递,&传递效率更高,构造函数记得使用 初值列,效率比在构造函数体里赋值效率高!
再考虑这个类有哪些功能函数,有什么能力,这个函数实现时都可以为内联函数(inline):
比如为复数设计一个 += 的操作,设计函数时,必须考虑到把该函数是设为类成员函数还是非成员函数;
我们再设计一个函数时,需要想函数后是否要加const,如果该函数不会改动数据的话,应该加const;
再考虑是否成为类成员函数时,如果这个函数用的参数都是该类,那可以写成成员函数,如果参数时有不包含该类的,需要是非成员函数;
如果后边有函数要直接取得类的private数据,可以声明friend;
4.在第3部分进行类成员函数定义时,如果是重载函数(例如 +=),需要写成 "返回类型 类名::operator +=" 的形式,然后开始思考参数;
如果是全局函数,写成 "返回类 函数名称"形式,其中上面的 "类名::operator +=" 其实就算函数名称;
首先 += 一定有左右两边,是作用在左边(操作符重载一定是作用在左边),(同类的话)那参数中会有一个隐藏参数this,this就是表示左边,然后写右边的参数,如果不同类的话,例如重载 << ,左边是 cout,那参数左边不能隐藏,要写cout属于的类 ostream&,像cout这样的可能会出现 cout << c1,把c1 输出到cout后,cout状态会改变,所以ostream& 不能是const;
写参数时,首先考虑是不是 &传递,可以就用 &传递, 然后考虑要不要加const,如果该参数不会改变的话,需要加const;
再考虑这个函数的返回值类型,再考虑返回值类型要不要加 &,如果返回的东西是原本就分配好内存的,是原本就存在的就应该加&,如果是在函数里新生成的local object,就不能加&;
在考虑返回值类型时,要考虑到这种情况,在上面的重载 << 中,会出现cout << c1 << c2; 的情况,先把c1输出到cout后,再把c2输出到cout,c1输出到cout后,需要还是cout所属ostream类,为了保障cout状态不改变,所以返回值类型应该为ostream,ostream本来就有的,要加上&;
再考虑要不要在函数前加inline,是类成员函数就加inline(大多都加inline,就算不是inline也不影响),是不是真的inline要看编译器;
如果返回的是一个新类对象,可以直接 "return 类名称 (里面写类参数的值);",这会生成一个新的临时类对象;
5.相同class对象互为friend;
返回的传递者无须知道接收者是以何种形式接收;
inline complex& //这里是以引用接收 __doapl(complex* ths, const complex& r) { ths->re += r.re; ths->im += r.im; return *ths;//返回的是值 }