1. 程式人生 > >C++學習筆記一

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返回型別
}