编写目标、以良好的方式编写C++
1、构造函数是否为初始化函数
2、函数该不该加const (该加就要加)(函数里头不会改变data,就要加)
3、参数的传递尽量用pass by reference 还有要不要加const
4、数据几乎没有例外要放在private里
5、函数绝大部分放在public里
Object Based (基于对象) (面对的是单一的class的设计)
classes的两种经典分类
1.class without pointer members ------Complex
2.class with pointer members ------String
Object Oriented(面向对象) (面对的是多重classes的设计,classes和classes之间的关系)
----继承 (inheritance)
----复合 (composition)
----委托 (delegation)
complex(复数)(不带指针) (不带指针的函数多半不用析构函数)
1. 有实部和虚部 (决定内存占用大小) (数据很多分)
2.加、减、乘、除、共轭、正弦...(函数处理实部虚部) (函数只有一份)
complex c1(2,1);
complex c2;
complex* pc = new complex(0,1);
string (字符串)(带指针)
1.字符 s (其实是个ptr,指向一串字符) (每一个数据里面只有一个指针)
2.拷贝,输出,附加,插入...
string s1("Hello ");
string s2("World ");
string* ps = new string;
C++ programs 代码的基本形式
Header(头文件)中的防卫式声明
complex.h
#ifndef __COMPLEX__
#define __COMPLEX__
... //guard(防卫式声明)
#endif
complex-test.h
#include <iostream>
#include "complex.h“
using namespace std;
int main {}
Header (头文件)的布局
#ifndef __COMPLEX__
#define __COMPLEX__
//前置声明
#include <cmath>
class ostream;
class complex;
complex&
__doapl (complex* ths, const complex& r);
//类的声明
class complex
{
...
};
//类的定义
complex::function ...
#endif
class 的声明 (declaration) 和 class template ( 模板) 简介
//T为类型
template<typename T>
class complex
{
public:
complex (T r = 0, T i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&);
T real () const { return re; }
T imag () const { return im; }
private:
T re, im;
friend complex& __doapl (complex*, const complex&);
};
{
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
...
}
inline (内置函数)
1.函数若在 class body內定义完成 , 便自动成为 inline
2.函数若想在 class body外定义inline,只要头上加inline
inline double imag(const complex& x)
{
return x.imag ();
}
编译器决定是否真的inline(即使你想inline,要看编译器的能力)
access level()访问级别
1、private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.
2、public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问.
3、protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问 .
初值列 、初始化列表(设初值)
constructor (ctor, 构造函数) 构造函数,无返回值类型
complex (double r = 0, double i = 0) //默认实参(如果后期没有给初值的话,就用默认参数)
: re (r), im (i) //initialization list 初值列 、初始化列表
{ } //赋值 //分配内存 ,开一个窗口、文件都有可能
一般不建议用赋值的办法(跳过了初始化的一步,效率变低)
complex (double r = 0, double i = 0)
{ re = r; im = i; } //assignments 赋值
ctor ( 构造函数) 可以有很多个 – overloading ( 重载)
1、同名函数可以重载(在编译器来看是不同名的)
2、函数重载常常发生在构造函数身上
3、如果构造函数已经拥有默认值,就不可以写如下会有冲突,但还是可以写其他的构造函数
complex () : re(0), im(0) { } //不可以,不知道要调用那个构造函数,会有冲突
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
//complex () : re(0), im(0) { } //不可以,不知道要调用那个构造函数,会有冲突
complex& operator += (const complex&);
double real () const { return re; } //
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
void real(double r) const { re = r; } //同名函数可以有多个
{
complex c1;
complex c2();
...
}
ctors 放在 private 里 (Singleton写法)
class A {
public:
static A& getInstance();
setup() { ... }
private:
A();
A(const A& rhs); //重载
...
};
A& A::getInstance()
{
static A a;
return a;
}
A::getInstance().setup();
const member functions ( 常量成员函数)
如果在编程中确实有某个值保持不变,就应该明确使用const
double real () const { return re; } //如果没有const,编译就可能会改变它的值就会出错
//函数里头不会改变data,就要加 //这里没有改动re,只是把它取出来
const complex c1(2,1);
const修饰成员函数
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量
参数传递:pass by value (有多大传多大) VS pass by reference (to const) (传引用相当于传指针)
最好所有的参数传递都传引用(快又漂亮)
class complex
{
public:
complex (double r = 0, double i = 0) //pass by value (没有任何特殊符号)
: re (r), im (i)
{ }
complex& operator += (const complex&); //pass by reference to const (&取地址且const不该)
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;
c2 += c1;
cout << c2;
}
ostream&
operator << (ostream& os, const complex& x) //pass by referencez
{
return os << '(' << real (x) << ','
<< imag (x) << ')';
}
返回值传递 :return by value VS return by reference(to const)
可以的情况下也尽量返回引用
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&); //return by reference
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
ostream&
operator << (ostream& os, const complex& x)
{
return os << '(' << real (x) << ','
<< imag (x) << ')';
}
friend (友元)
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&);
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
inline complex&
__doapl (complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
相同 class 的各个 objects(对象) 互为 friends ( 友元)
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;
};
{
complex c1(2,1);
complex c2;
c2.func(c1);
}
class body 外的各种定义(definitions)
do assignment plus
inline complex&
__doapl(complex* ths, const complex& r)
{
ths->re += r.re; //第一个参数不会被改变,第二个参数不会被改变
ths->im += r.im; //注意(+和=)与(+=)的区别
return *ths; //返回对象不会消亡,就用return by reference
}
inline complex&
complex::operator += (const complex& r)
{
return __doapl (this, r);
}
二元操作符(操作符相当于函数,+=)
操作符作用在左边身上,所以左边的参数就被隐藏的放进来,所以只要写右边就好了
operator overloading ( 操作符重载之1, 成员函数) this
所有的成员函数都一定带着一个隐含的参数(this不能写出来)
// += 的动作
//操作符设计,标准库就含有如下代码,我们只是把他调出来,方便其他类调用
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) //但参数列不可能写出来this
//操作符作用在左边身上,所以左边的参数就被隐藏的放进来,所以只要写右边就好了
// 且右边又不需要动,所以引用参数要加const
{
return __doapl (this, r); //函数可以用this
//返回得到左边这种东西(就是复数)
//我们把这个函数要做的事情再推个上面的函数,所以传出去的值类型不会变
}
//你所传出去的东西,如果local object(这个函数本体创建的东西,那就可以传引用)
//右边加到左边身上,左边本来就存在,所以不是local object,就可以传引用
//而且这个函数创建在本体之外,所以可以加上inline,至于会不会真的成为inline,那是编译器自己的事
{
complex c1(2,1);
complex c2(5);
c2 += c1; //将 c1 加到 c2 上
}
inline complex& //等价于上面第二个串代码
complex::operator += ( this, const complex& r) //c2 就是(this) += c1(r)
{ //操作符作用在左边身上,所以左边的参数就被隐藏的放进来,所以只要写右边就好了, 且右边又不需要动,所以引用参数要加const
return __doapl (this, r); //返回得到左边这种东西(就是复数)//我们把这个函数要做的事情再推个上面的函数,所以传出去的值类型不会变你所传出去的东西,如果local object(这个函数本体创建的东西,那就可以传引用),右边加到左边身上,左边本来就存在,所以不是local object,就可以传引用,而且这个函数创建在本体之外,所以可以加上inline,至于会不会真的成为inline,那是编译器自己的事
}
return by reference 语法分析
传递者无需知道接收者是以什么(reference)形式接受
// += 的动作
inline complex& //接受端 //返回声明返回的是reference //返回类型
__doapl(complex* ths, const complex& r) //左操作数是一根指针
{
...
return *ths; //传出指针所指的东西 //return by value
}
inline complex&
complex::operator += (const complex& r) //pass by reference (to const)
{
return __doapl(this,r);
}
{
complex c1(2,1);
complex c2(5);
c2 += c1; //将 c1 加到 c2 上
}
c3 += c2 += c1; //如何是连串赋值,就不可以是value
class body 之外的各种定义 (definitions)
inline double imag(const complex& x) //习惯用引用去传reference
{
return x.imag ();
}
inline double real(const complex& x)
{
return x.real ();
}
{
complex c1(2,1);
cout << imag(c1);
cout << real(c1);
}
operator overloading ( 操作符重载之2, 非成员函数) (无this)
+ 左加右,既不能放再左身上,也不能放在右身上,必须创建一个新的东西给它(放在函数里头新创建的对象),所以它是一个local,就一定不能传引用,即return by value
inline complex
operator + (const complex& x, const complex& y) //全局函数
{
return complex (real (x) + real (y), imag (x) + imag (y)); //c2 = c1 + c2;
//complex ()创建一个新的对象object,任何把创建的对象返回 ----临时对象
//如果你把函数写在class里头就没法运用,复数加实数,实数加复数
}
inline complex
operator + (const complex& x, double y)
{
return complex (real (x) + y, imag (x)); //c2 = c1 + 5;
}
inline complex
operator + (double x, const complex& y)
{
return complex (x + real (y), imag (y)); //c2 = 7 + c1;
}
{
complex c1(2,1);
complex c2;
c2 = c1 + c2;
c2 = c1 + 5;
c2 = 7 + c1;
}
temp object (临时对象) typename ();(相当于 int() ) 标准库一般用的多
下面这些函数绝不可以 return by reference ,因为, 它们返回的必定是个 local object.
complex ()创建一个新的对象object,任何把创建的对象返回 ----临时对象
//左边和右边相加,但是加的结果没处放,就需要创建出来,准备放结果
//前面是右边加到左边身上,所以右边已经存在啦
inline complex //在complex中创建结果,离开complex函数就会死亡,外界没法用
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y), imag (x) + imag (y)); //return by value
//complex ()创建一个新的对象object,任何把创建的对象返回 ----临时对象
}
inline complex
operator + (const complex& x, double y)
{
return complex (real (x) + y, imag (x)); //return by value // typename ()
}
inline complex
operator + (double x, const complex& y)
{
return complex (x + real (y), imag (y)); //return by value // typename ()只不过有参数
}
{
int(7);
complex c1(2,1);
complex c2;
complex(); //typename ()
complex(4,5); //typename ()
cout << complex(2); //typename ()
}
class body 之外的各种定义 (definitions)
inline complex //可以pass by reference,没有产生新的结果
operator + (const complex& x) //正号 ,一个参数就是正号
{
return x;
}
//这个函数绝不可return by reference ,因为其返回的必定是个 local object
inline complex
operator - (const complex& x) //负号,一个参数
{
return complex (-real (x), -imag (x)); // typename(),一个临时对象
}
{
complex c1(2,1);
complex c2;
cout << -c1; //创建复数之后可以取反(negate反向)
cout << +c1;
}
operator overloading ( 操作符重载,==相不相等), 非成员函数
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;
}
{
complex c1(2,1);
complex c2;
cout << (c1 == c2);
cout << (c1 == 2);
cout << (0 == c2);
}
operator overloading ( 操作符重载,!==不相等), 非成员函数
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;
}
{
complex c1(2,1);
complex c2;
cout << (c1 != c2);
cout << (c1 != 2);
cout << (0 != c2);
}
operator overloading ( 操作符(作用在左边身上)重载), 非成员函数
cout 就是ostream& os
//共轭复数(实部相等,虚部±相反)
inline complex
conj (const complex& x) //全局函数
{
return complex (real (x), -imag (x));
}
//<<
#include <iostream.h>
ostream& //不能加const ,有cout在改变
operator << (ostream& os, const complex& x) //os一直在改变所以ostream& os不能加const
{
return os << '(' << real (x) << ','<< imag (x) << ')'; //os一直在改变
}
{
complex c1(2,1);
cout << conj(c1); //只能用全局写法
cout << c1 << conj(c1); //从左到右,因为可以连续传值,所以不设为void返回类型
}