- 写在前面
- Object Oriented
class 的分类:带指针的class和不带指针的class,
class 的声明
这里有一个inline的概念,写在类里面的默认为inline,写在class外部的只有声明,为inline才有可能编译成Inline,最终编译时是否为inline,由编译器决定。Inline函数速度快.
public:
private:所有数据都应放在private,函数根据是否想要被外界调
使用对象
× √
- 构造函数:
想要创建一个对象,会自动调用的函数就是构造函数,如上,构造函数与类同名,没有返回类型,不需要有,因为构造函数就是要来创建对象的,此处对象就是complex,上面定义的构造函数使用了列表初始化,并且这种初始化只有构造函数才有,其它函数是没有的,并且给出了default argument
这里多说一些,一个数值的设定有两个阶段,一个是初始化,一个是后面再赋值(assign),所以列表初始化和在函数体赋值是不同的。
与构造函数对应是析构函数,但是这个例子中不需要写析构函数,前面提到过class的经典分类,不带指针的class多半需用写析构函数
构造函数可以有多个-overloading,但是存在歧义的不可以,如下:
complex() :re(0), im(0) {}//不可以,ambiguous
如下面的对象c2,都没有给参数,编译器发现 可以调用上面的,但是编译器也发现class中的构造函数虽然有参数,但是有默认值,也可以调用,因此编译器就不知调用哪个了
创建对象
构造函数放在private中,即不允许外界调用构造函数
class A { public: static A& getInstance(); private: A(); A(const A& rhs); }; A& A::getInstance() { static A a; return a; }
外界只能用一份A,即static的部分,外界用的时候不能使用传统的调用方式,只能通过getInstance()函数
- 参数传递与返回值
double real()const { return re; } double imag()const { return im; }
看上面的代码,都是const型的,因为我不需要改变数据,只是将数据拿出来,看下面两种情况
A. B.
如果我的函数没用定义为const类型,B中的调用就是错误的,使用者本想,我只是返回数据,并不改变,所以创建了一个常对象,但是由于函数没有定义为const,所以就会出现调用错误。
- 参数传递:
pass by value vs pass by reference(to const)
pass by value 就是整包都传过去,数据多大就传多大,传的数据是压到static栈中的,本例中,传的是double,是4个字节,传的就是4个字节,若传的数据很大呢,效率就低,所以尽量不要pass by value, 可以pass by reference ,reference在底部就是指针,
1 ostream& operator <<(ostream& os, const complex& x) 2 { 3 return os << '(' << real(x) << ',' << imag(x) << ')'; 4 } 5 int main() 6 { 7 complex c1(2, 1); 8 complex c2; 9 c2 += c1; 10 cout << c2; 11 }
第7,8行会调用构造函数,通过pass by lalue的方式,第9行会调用1-4行的函数,pass by reference的方式。返回值的传递也尽量pass by reference
friend complex& __doapl(complex*, const complex&);//return value :pass by reference
后面会给出上面函数的detail
下面看一种情况,为什么可以这样使用
int func(const complex& param) { return param.re + param.im; } //用法 complex c3; c3.func(c1);
可以这样理解:相同class的各个objects 互为friends
class body外的各种定义什么情况下可以pass by value,什么情况下可以pass by reference
首先看看什么情况不能return by reference
1 inline complex& 2 __doapl(complex* ths, const complex& r) 3 { 4 ths->re += r.re;//第一参数将会被改动 5 ths->im += r.im;//第二参数不会被改动 6 return *ths; 7 } 8 inline complex& 9 complex::operator+=(const complex& r) 10 { 11 return __doapl(this, r); 12 }
这里,函数将结果放在第一个参数中,有专门的存储的空间,还有一种情况就是函数专门开辟一个空间存放结果,如果只是
return c1 + c2;
这时,是一个临时开辟的local变量存储c1+c2的结果,函数结束,这个临时变量的生命就结束了,这时,就不能返回reference
- 操作符重载(-1,成员函数) this
想象我们在数学中的复数,有哪些操作,比如相加,复数+复数,复数+实数,复数+纯虚数,
继续看上面代码,编译器看到c2+=c1,就会把操作符作用到左边c2身上,上面的inline complex& complex::operator+=(const complex& r){}相当于
inline complex& complex::operator(this,const complex& r){}
为什么可以这样呢,这就是上面辩题中写道的操作符重载之成员函数,因为所有成员函数带有一个隐藏的参数this,谁调用这个函数谁就是this,这里this是c2,
Note:this不可以写在参数列表中,用可以像函数体中那样用,return __doapl(this,r);
- return by reference 语法分析
继续看上面的代码,看函数定义时的返回类型是complex&,函数体内返回的却是*ths,这样是正确的,因为,传递者无需知道接收者是以reference形式接收
class body之外的函数定义
operator overloading(操作符重载-2,非成员函数)无this
考虑到用户的行为,定义了三种+ 形式
1 inline complex 2 operator +(const complex& x, const complex& y) 3 { 4 return complex(real(x) + real(y), imag(x) + imag(y)); 5 } 6 7 inline complex 8 operator +(const complex& x, double y) 9 { 10 return complex(real(x) + y, imag(y)); 11 } 12 13 inline complex 14 operator +(double x, const complex& y) 15 { 16 return complex(x + real(y), imag(y)); 17 }
如上,对于上面的操作符重载,没有this pointer,因为是全域函数,不再是成员函数
- temp object (临时对象)tempname()
再看上面的代码,return by value,对于上面的代码,绝不可以return by reference,因为他们返回的一定是local object,因为加的结果是函数临时创建的,函数结束,临时变量死亡,
解释一下:tempname(),此处是complex(),就是创建临时对象,tempname()类似于Int(),
operator +(const complex& x) { return x; } inline complex operator -(const complex& x) { return complex(-real(x), -imag(x)); } //使用 { complex c1(2, 1); complex c2; cout << -c1; cout << +c1; }
这里是return by value,这里可是return by reference,因为,这里结果没有改变,也没有创建新的临时对象,所以是可以return by reference的。
接下来还是操作符重载,考察的东西一样,语法上没有新的了,
inline bool operator ==(const complex& x, const complex& y) { return real(x) == real(y) && imag(x) == iamg(y); } inline bool operator ==(const complex& x, double y) { return real(x) == y && iamg(x) == 0; } inline bool operator ==(double x, complex& y) { return x == real(y) && iamg(y) == 0; } inline bool operator !=(const complex& x, complex& y) { return real(x) != real(y) || imag(x) != imag(y); } inline bool operator !=(const complex& x, double y) { return real(x) != y || iamg(x) != 0; } inline bool operator !=(double x, complex& y) { return x != real(y) || iamg(y) != 0; } inline complex conj(const complex& x) { return complex(real(x), -imag(y)); }
ostream& operator <<(ostream& os, const complex& x) { return os << '(' << real(x) << ',' << imag(x) << ')'; } //使用 { cout << conj(c1); cout << c1 << conj(c1); }
"<<"作用在左边,这里绝不能写成成员函数,必须写成全域函数。os也不可是const的,因为每往cout中放入“东西”的时候都是在改变os中的内容,若将返回类型 ostream& ,改为 void 可以吗?
如果只是输出一个是可以的(上面第一处使用),如果连续输出是不可以的(第二处使用),
下面是完整的代码(还有很多操作没写,但写的已经涵盖了这种类型的class的几乎所有语法,有时间再补全):
1 #pragma once 2 #ifndef __COMPLEX__ 3 #define __COMPLEX__ 4 5 class complex { 6 public: 7 complex(double r = 0, double i = 0) :re(r), im(i) {}//construct function 8 //complex() :re(0), im(0) {}//不可以,ambiguous 9 complex& operator +=(const complex&);//two choices,member func or non-member func, 10 //here is member func,do not need to change the value,so is const 11 double real()const { return re; } 12 double imag()const { return im; } 13 int func(const complex& param) 14 { 15 return param.re + param.im; 16 } 17 private: 18 double re, im; 19 friend complex& __doapl(complex*, const complex&);//return value :pass by reference 20 21 }; 22 23 inline complex& 24 __doapl(complex* ths, const complex& r) 25 { 26 ths->re += r.re;//第一参数将会被改动 27 ths->im += r.im;//第二参数不会被改动 28 return *ths; 29 30 } 31 inline complex& 32 complex::operator+=(const complex& r)//the right is not change,so is const,the left 33 {//has already existed,so return by reference 34 return __doapl(this, r); 35 } 36 inline double 37 real(const complex& x) 38 { 39 return x.real(); 40 } 41 inline double 42 imag(const complex& x) 43 { 44 return x.imag(); 45 } 46 //"+"has more than one cases,so set it as the non-member func 47 inline complex 48 operator +(const complex& x, const complex& y) 49 {//the sum result will be put in a local variable,so return by value 50 return complex(real(x) + real(y), imag(x) + imag(y)); 51 } 52 53 inline complex 54 operator +(const complex& x, double y) 55 { 56 return complex(real(x) + y, imag(y)); 57 } 58 59 inline complex 60 operator +(double x, const complex& y) 61 { 62 return complex(x + real(y), imag(y)); 63 } 64 inline complex 65 operator +(const complex& x) 66 { 67 return x; 68 } 69 70 inline complex 71 operator -(const complex& x) 72 { 73 return complex(-real(x), -imag(x)); 74 } 75 76 inline bool 77 operator ==(const complex& x, const complex& y) 78 { 79 return real(x) == real(y) && imag(x) == imag(y); 80 } 81 inline bool 82 operator ==(const complex& x, double y) 83 { 84 return real(x) == y && imag(x) == 0; 85 } 86 87 inline bool 88 operator ==(double x, complex& y) 89 { 90 return x == real(y) && iamg(y) == 0; 91 } 92 93 inline bool 94 operator !=(const complex& x, complex& y) 95 { 96 return real(x) != real(y) || imag(x) != imag(y); 97 } 98 99 inline bool 100 operator !=(const complex& x, double y) 101 { 102 return real(x) != y || imag(x) != 0; 103 } 104 105 inline bool 106 operator !=(double x, complex& y) 107 { 108 return x != real(y) || imag(y) != 0; 109 } 110 111 inline complex 112 conj(const complex& x) 113 { 114 return complex(real(x), -imag(x)); 115 } 116 117 #endif
测试代码有时间再补