《EffectiveC++》讀書筆記(一)條款1-3

前言

想一想C++也學了大半年但博客沒怎麼寫過C++方面的。一是當初自己看《C++ Primer》很懵,寫博客也只是抄書,二是後來代碼量上去了,踩了各種坑之後也收穫的很多,更多的都是要牢牢記住的基礎用法,沒啥整理的必要。。。

對於C++的學習,《Effective C++》絕對是一本必看的書,但又不是和《C++ Primer》同時看,而是在有了一定的C++基礎和代碼量之後,對於基礎語法,知識點都有所掌握,但是不知道如何正確使用C++時,再深入閱讀,這時收穫更多。

同時,這本書所講述的並不是一些規定,而是有效運用C++的方法,所以需要仔細的理解和體會,因此,希望通過整理讀書筆記深入理解其內容。

正文

Item 1: View C++ as a federation of Languages

這裏侯捷老師翻譯爲“視C++爲一個語言聯邦”,我覺得確實很正規,也很準確,但是對於我們理解上可能並不容易(聯邦。。。)。

C++,有很多人愛它,但好像更多人Diss它,在國內遍地Java培訓機構,但卻少有C++的培訓班,我想有很大一部分原因就是C++的內容太多了,不僅向現代高級語言看齊,又要兼容古老的C。

我們可以把它分爲四大部分:
1。 C,C++的設計就是要和C兼容,以至於很多地方做的很XX,不如Java等方便,但是好處就是和OS更加親近了。
2。OO 也就是從C到C++最大的不同吧,有class,有繼承,多 態,封裝。
3。泛型,主要就是模板(template),對於寫庫來說是個重頭戲。
4。STL,一門編程語言想要流行開來必須要有一套高效,便利的輪子(庫),當然對學習者來說也是優秀代碼的寶庫,STL符合這兩點,(iostream可能。。。)。

在我們實際使用C++時往往只會用其中的一部分,比如ACMer可能就是C部分加上STL,而在實際工程中可能主要是OO,庫的作者則主要是template,所以C++就像一個大工具箱,選好你適合的工具,當然,還要遵從工具的使用規約。。。

Item 2: Prefer consts,enums,inlines to #defines

記得C語言課上,講到#define時老師往往會說把多次出現的常量用#define定義(比如PI),那麼爲什麼不用const呢(黑線)?

使用const的好處如下:
1.調試更加方便,由於宏是在預處理階段被CPP展開,所以也許編譯器並不知道,如果你在使用這個常量的過程中遇到問題,你完全是矇蔽的,尤其是使用到別人的代碼時。
一個形如9.8的常量你可能並不知道有什麼特殊意義,但是一個const 常量是由編譯器處理的,將被記錄到symbol table裏,方便我們之後的調試(能看到變量名可以定位bug發生點)。

2.減少目標代碼長度,盲目地將宏替換可能導致目標代碼會出現多份,而一個const常量則不會。(這裏不是很懂爲什麼宏替換會出現多份)

同時,對於C++裏的常量,#define有其無法完成之事,即限定作用域(類作用域)的常量,也就是說對於類內的專屬常量,#define並不能限定其作用域。
我們當然可以通過#undef這種機制來限制宏的作用域,但是對於一個類內的常量確無法做到。
而const則可以。

當我們需要用到這個這個常量時必須要加上作用域運算符,這就限制類常量的作用域。

值得一提的是,作爲一個static成員變量,我們知道其需要在類內聲明,在類外定義,這樣才保證同一個類的多個對象共享一個static成員變量。
然而對於static const int/char/bool 類型的常量可以直接在類內定義(書上說是較新的編譯器,但本書成於2005年。。。)

class A{
public:
  static const int size = 2; //正確
  static const float Pi = 2.3;//錯誤
};

但如果你需要的是類內常量是在類內使用或者你的編譯器真的不支持這種in class定義,就需要enum出場了。

class A{
public:
  //static const int size = 2; 
  enum{ 
      size = 2//通過enum來設置類內使用的類內常量
  };
  char myarray[size]
};

enum所定義的常量名和const一樣,也進入symbol table,方便我們調試。

說起enum,還有一個優點,不過這也是針對舊或者“不夠優秀”的編譯器,便是enum中的常量不能被&或者被引用綁定,const則可能,但我在自己的g++測試時,const常量也無法被引用綁定或者&,所以感覺enum的好處,enmmmm基本被const替代。

而老師講到#define時,可能還會說到另一種用法

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b)) // 調用函數f,將ab之中的較大值作爲參數調用f

小組的面試也出過這樣的問題,當然,我們都知道這樣的宏會帶來怎樣的副作用(傳入一個i++)。

這裏的#define是爲了,呃,實現C語言中的泛型,and代碼很短不用編寫成函數,但是我們來到C++了,對於泛型,我們有template,而很短的代碼則編寫爲就地展開的inline函數。

template <typename T>
inline void callWithMax(const T &a, const T &b)
{
    f(a > b ? a : b);
}

同樣,當這樣的函數變爲類內專屬時,#define一樣無能爲力。

但是CPP還有其不可替代的作用,包括#ifdef,包括與C兼容,包括看ACMer的代碼總會一大堆宏。。。

Item 3: Use const whenever possible

const確實還是很好用的,當你需要規定一個東西爲常量時,設置成const就讓編譯器替你做檢查了。

對於重載的運算符,我們讓其返回的對象爲const可以避免一些無謂的錯誤。

const Object operator*(Obeject &a, Object &b)

// 一個手誤的操作,將因爲const被制止
if((a*b) = c)

C++的重載機制支持對於參數有無const的重載,和調用對象是否爲const的重載。

int fun(char *a)
{
    return 2;
}
int fun(const char *a) //若兩種都有,則能重載,若只有const 參數版本,則按照C風格可以傳非const參數
{
    return 1;
}

class A{
    // 即按照調用對象是否爲const重載
    const char &operator[](int b) const
    {
        return test[b];
    }
    char &operator[](string t)  
    {
        int a =2;
        return test[a];
    }
    string test;
};

需要注意的是,這裏能調用const成員函數的const對象,指的是bitwise constness,也就是對於對象的每一個成員變量都無法改變其任何一個bit。

但若對象有一個指針變量呢?其指向的對象是否視爲本對象的一部分呢?

所以const對象也可以有變化的成員變量,通過mutable關鍵字聲明。

而const成員函數和non-const成員函數往往只有些許不同,所以我們可以只編寫const成員函數,而對於non-const成員函數通過調用const成員函數+cast。

一個例子如下

const Object &operator[](std::size) const
{
    // 老實實現
}
Object &opeartor[](std::size pos)
{
    //static_case<const Object>(*this)[pos] 是將non-const對象轉爲const對象來調用const成員函數
    //const_cast將其返回的const Object &的const 移除,來符合函數的返回值類型
    return const_cast<Object &>(static_cast<const Object>(*this)[])
}

但是堅決不要通過const成員函數調用non-const成員函數(你懂的)。

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