Effective C++ 读书笔记1

来源:互联网 发布:ipad文献阅读软件 编辑:程序博客网 时间:2024/06/05 21:16

条款1 视C++为一个语言联邦

     今天的C++已经是个多重范型编程语言,一个支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。为了理解C++,必须认识其主要的次语言,总共4个:

1.C

2.Object-Oriented C++

3.Template C++

4.STL


条款2 尽量以const, enum, inline替换#define(即宁可以编译器替换预处理器)

1.

       #define ASPECT_RATIO 1.653

       ASPECT_RATIO没有进入编译器的symbol table,错误信息会提到1.653而不是ASPECT_RATIO,这样查找这个符号就会花费时间,所以应该这样:

const double AspectRatio = 1.653

      有两种特殊情况:

      一。第一常量指针。应该写为:const char* const authorName = "Scott Meyers";

      二。class专属常量。为了确保次常量最多只有一份实体,必须让它成为一个static成员。通常C++要求你对所使用的任何东西提供一个定义式,但如果它是class专属常量又是static且为整数类型(int, char, bool),则需要特殊处理:只要不去它们的地址,可以声明并使用它们而无需提供定义式。      

class GamePlayer{      private:            static const int Num = 5;//常量声明式            int scores[Num];}


class TestClass{private:static const double d = 1.2;//这里居然可以初始化double类型的,但是在公司电脑的eclipse上就不行,奇怪,同是cdt,版本也相同static const int i = 22;};//const double TestClass::d = 1.2; //此时不能加static,static只用于声明,此处是定义;同理,不能在定义静态函数时加上static;但是改法则不适用于全局static 函数     如果编译器不支持static整型常量在class内的初值设定,可改用所谓的“the enum hack”补偿做法。
class GamePlayer{     private:           enum {NumTurns = 5};           int scores[NumTruns];};
2.#define常用来实现宏,看起来像函数,但是不会导致函数调用的开销
  #define CALL_WITH_MAX(a,b) f((a)>(b)? (a):(b))  //以a和b中的最大值调用函数f
   int a = 5, b = 0;
   CALL_WITH_MAX(++a, b); //a被累加两次      CALL_WITH_MAX(++a, b+10); //a被累加一次       可以使用template inline来替代上述表示://template可以实现和参数类型无关,inline可以消除函数调用的开销  
 template<typename T>inline void CallWithMax(const T&a, const T& b){    f(a>b? a: b)}

 有了const, enum 和inline,只能降低对预处理器的需求,但不能完全消除,#include是必需品,而#ifdef/#ifndef在控制编译中扮演重要角色
   
条款3  尽可能使用const

1. 注意:    如果被指物为常量,有两种写法:    const int* cpi;    int const* cpi;注意不是int *const cpi;2. const成员函数   将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象。该类函数比较重要,因为:一,得知哪个函数可以改动对象内容哪个不行很重要;二,使操作const对象成为可能(条款20说,改善C++程序效率的一个根本方法是以pass by reference-to-const方式传递对象,而此技术可行的前提是,我们有const成员函数可用来处理取得的const对象)许多人漠视一件事实:两个成员函数如果只是常量性不同,可以被重载,这是一个重要的C++特性:    


class TestBlock{public:const char& operator[](size_t position) const{return text[position];}char& operator[](size_t position){return text[position];}private:string text;};
void print(const TextBlock* ctb){std::cout << ctb[0];//此处调用的是const operator[]版本的:不太明白,函数的参数个数和类型完全相同,编译器是如何判断该调用哪个版本的operator[]的呢?}


void print(){    TextBlock tb;     tb[0] = 'x';//注意:如果non-const operator[]返回类型不是reference-to-char,而不是char,那么这里将会出错,因为如果函数返回值是内置类型,那么改动函数返回值从来都是不合法的。貌似有点说不通,为什么不能修改函数反悔值,却能修改函数返回的reference?? }const成员函数的两个阵营:一是bitwise(又称为physical constness),认为成员函数不能更改对象内的任何一个bit,不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试,也就是说,一个更改了指针所指物的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么称此函数为bitwise const不会引发编译器异议。
class CTextBlock{public:    char& operator[] (std::size_t position) const{                         return pText[position];     }private:     char* pText;}const CTextBlock cctb("hello");char* pc = &cctb[0];*pc='j' //虽然调用了const函数,没有改变内部成员变量,但返回的指针最终还是导致改变了成员的值二是logical constness,主张一个const成员函数可以修改它所处理对象内的某些bits:
class CTextBlock{public:std::size_t length() const;private:char* pText;mutable std::size_t testLength;mutable bool lengthIsValid;};std::size_t CTextBlock::length() const{if(!lengthIsValid){testLength = std::strlen(pText);//可以修改成员变量lengthIsValid = true;//可以修改成员变量}};2.在const和non-const成员函数中避免重复   上述的实现中两个相互重载的函数造成了代码的重复,为了实现代码复用,可以使用non-const版本调用const版本:
class TextBlock{public:    const char& operator[] (std::size_t position) const {             //...             //....             //....             return text[position];      }      char& operator[] (std::size_t position){              return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);//为*this加上const,调用const op[]:这里不是很理解,为什么一定需要把*this转为const TextBlock&????       }};    上述代码有两次类型转换,第一次用来为*this添加const(这使接下来条用operator[]时得以调用const版本);第二次则是从const operator[]返回值中移除const;更值得了解的是,反向做法------令const版本调用non-const版本以避免重复是不应该的。const成员函数承诺绝不改变其对象的逻辑状态,non-const成员函数却没有这般承诺。如果在const函数调用non-const函数:你曾经承诺不改动的那个对象被改动了。实际上若要令这样的代码通过编译,你必须使用一个const_cast将*this身上的const性质解放掉(????)