C++學習筆記一
編寫目標、以良好的方式編寫C++
1、建構函式是否為初始化函式
2、函式該不該加const (該加就要加)
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,編譯就可能會改變它的值就會出錯
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 referencedouble 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
{
return __doapl (this, r); //函式可以用this
}
{ complex c1(2,1); complex c2(5);c2 += c1; //將 c1 加到 c2 上 }
inline complex& //等價於上面第二個串程式碼 complex::operator += ( this, const complex& r) //c2 就是(this) += c1(r) { return __doapl (this, r); }
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)
inline complex
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y),
imag (x) + imag (y)); //c2 = c1 + c2;
}
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.
//左邊和右邊相加,但是加的結果沒處放,就需要創建出來,準備放結果
//前面是右邊加到左邊身上,所以右邊已經存在啦
inline complex //在complex中建立結果,離開complex函式就會死亡,外界沒法用
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y), imag (x) + imag (y)); //return by value
}
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返回型別
}