C++程序設計(下)第一週

@(boolan C++)[C++]

1.轉換函數(conversion fuction)

轉換函數一般是類的成員函數。轉換函數的作用是,把class轉化成某種類型,也可以把其他類型轉換爲class。

例如,設計一個函數Fraction,在class Fraction裏,定義了一個函數,operator double()const {..}這個函數就是轉化函數,它的用處是,如果需要把fraction這個類轉爲double,就調用這個函數;


class Fraction
{
public:
 Fraction(int num, int den=1)
   : m_numerator(num), m_denominator(den)
 { cout << m_numerator << ' ' << m_denominator << endl; }
 
  operator double() const {
      return (double)m_numerator / m_denominator;
  }

private:  
   int m_numerator;    //
   int m_denominator;  //
};


此處便是轉化函數起作用的地方。在第一條語句中,構造了一個對象f,下一條語句中,出現了4+f,這時,f並不是double型,Fraction類也沒有重載 加號,編譯器本應該報錯,但是編譯器在Fraction類裏頭找到了operator double()const {..} 有了這個函數,第二條語句就不會報錯,因爲編譯器會調用這個函數,把f轉化爲double型。

Fraction f(3,5);
 
 double d = 4 + f;  //4.6
 cout << d << endl;

只要認爲合理,類的轉換函數可以有多個,轉換類型不一定要是基本類型,複合類型也可;


2.non-explict-one-argument ctor

還是用的前面的class Fraction說明。在這裏,Fraction的構造函數有兩個參數,其中一個有了默認值,默認是1;因此在初始化Fraction對象的時候可以只給一個參數賦值。

class Fraction
{
public:
  Fraction(int num, int den=1)
   : m_numerator(num), m_denominator(den)
 { cout << m_numerator << ' ' << m_denominator << endl; }


  Fraction operator+(const Fraction& f) { 
    cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  
    return f;
 }

private:  
   int m_numerator;    //
   int m_denominator;  //
};

在下面的第二條語句中,Fraction重載了加號,但加號左邊是一個常量而非Fraction對象,編譯器本該報錯。但由於Fraction的構造函數可以只有一個參數,所以編譯器會自作主張,調用它的構造函數,把4傳給第一個參數,這樣4+f也是沒有錯的,下面的語句可以通過。但是如果是第三種情況,即operator double()const {..} 和帶默認參數的構造函數同事存在,double b = 4+f就會報錯。因爲編譯器不知道是該將f轉化爲double型,還是將4轉化爲Fraction對象。

Fraction f(3,5);
 double d = 4 + f;  //4.6
 cout << d << endl;

但是如果是第三種情況,即operator double()const {..} 和帶默認參數的構造函數同事存在,double b = 4+f就會報錯。因爲編譯器不知道是該將f轉化爲double型,還是將4轉化爲Fraction對象。所以爲了避免出現這種錯誤,一般會在構造函數前面加一個關鍵字explict 。它一般用在構造函數或者模板前面,它告訴編譯器,構造函數只有在明確表明要調用的時候纔可以調用它;這時,編譯器就不會把因爲遇到4+f這樣的情況而去調用它的構造函數了。


3.關於智能指針

template<class T>
class shared_ptr
{
  public:
     T& operator*() const{return *px;}
  T* operator->() const {return px;}
  shared_ptr(T* p): px(p) {}
  private:
     T* px;
  long* pn;
}

struct Foo{... void method(void){} };


上面寫了一個智能指針類。下面語句的意思是:new一個Foo之後的指針,當成初值,賦值給一個智能指針;

因爲智能指針類裏面包含指針,所以凡是智能指針,都會重載*和->;

shared_ptr<Foo> sp(new Foo);

4.模板

C++裏面的模板可以分爲三大類:類模板、函數模板以及成員模板。

類模板,顧名思義,就是在設計類的時候將其中某個成員變量的類型聲明爲模板;函數模板在使用的時候不必指明和數據類型;而成員模板指的是結構體或者類模板裏面包含的模板;


5.三個主題

(1)variadic templates:數量不定的模板參數

在C++11標準中,新增加了一種語法 ... 這個語法的意思是我不確定我又多少個,所以用...表示省略。

在下面的語句中,我們可以看到template的第一個參數是typename T,第二個參數是typename... Type,這表明設計者在設計初期也不確定其參數個數,只有到了使用的時候才知道。在print函數裏, 函數體的第一條語句是輸出第一個參數,然後第二條語句,print函數調用自己,參數便是第一次調用print剩下的那些參數,即:const Type&...args ,這些值依次傳給print函數,它依次將它們輸出。


template<typename T,typename.. Types>

void print(const T&firstArg,const Types&...args)
{
   cout<<firstArg<<endl;
   print(args...);
}

測試:
print(7.5,"hello",bitset<16>(377),42);
結果:
7.5
hello
0000000101111001
42


(2)auto關鍵字

一般來說,如果我們聲明一個容器,並且需要用到迭代器,那麼,應該像下面這樣寫:

list<string> C;
list<string>::iterator it;
it = find(c.begin(),c.end(),target);


但是如果覺得寫那麼多太麻煩,還可以用auto關鍵字簡化,寫成下面這樣。auto關鍵字用處是,設計者可以不明確寫出變量的類型,編譯器會根據上下語句自行判斷。

就像下面的語句,find會返回一個迭代器,所以編譯器知道it的類型是迭代器,因此我們不必像上面那樣將it聲明爲迭代器。

但是要注意的是,儘管用auto很省事,但是不能全部用它,否則很容易出現混亂。


list<string> c;
...
auto it = find(c.begin(),c.end(),target);


還有一種情況,下面的寫法是不正確的。auto只能用在編譯器可以明確判斷出變量的類型的地方,而不能用來聲明變量。用auto聲明變量,編譯器也不會知道這個變量是什麼類型的。

list<string> c;
auto it;
it = find(c.begin(),c.end(),target);(不正確)

(3)ranged-banse for(C++11)

這也是C++11的新語法

for(decl : coll)
{statement}

for(int i : {2,3,4,5,6,7,8})
{cout << i << endl;}


6.引用 --用法小結

(1)引用一定要賦初值;
(2)引用一旦綁定對象後將不能再綁定到其他對象;

(3)引用是對象的一個代表 ,所以引用的地址、大小都和對象相同,但都是假象;

(4)我們很少用引用聲明變量,一般用於參數類型和返回類型;

(5)下面被視爲有“相同簽名”(所以二者不能同時存在),這是因爲這兩個函數在調用的時候語法是一樣的,都是 image(im);
double image(const double& im){...}
double image(const double im){...}

如果同時聲明瞭這兩個函數,那麼在調用次函數的時候,會產生二義性;

不過如果加了const就不會了,const也是函數簽名的一部分,函數簽名有沒有const是不一樣的。








































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章