Effective<1>——让自己习惯c++

来源:互联网 发布:nativeshare.js 回调 编辑:程序博客网 时间:2024/06/14 14:49

<1>让自己习惯C++


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

今天的c++已经是个多重范型编程语言,一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。主要可以分为四个次语言:
1.C。C++仍是以C为基础。区块(blocks)、语句(statements)、预处理器(perprocessor)、内置数据类型(built-in data types)、数组(arrays)、指针(pointers)等。
2.Object-Oriented C++。C with Classes所诉求的:classes(包括构造函数和析构函数)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)等。
3.Template C++。泛型编程部分。template相关考虑和设计已经弥漫整个C++。
4.STL。STL是个template程序库。它对容器(containers)、迭代器(iterators)、算法(algorithms)以及函数对象(functionobjects)的规约有极佳的紧密配合与协调。
~C++高效编程守着视状况而变化,取决于你使用C++的哪一部分。

条款02:尽量以const、enum、inline替换#define

常量替换#define有两种特殊情况。
1.定义常量指针(constant pointers),由于常量定义式通常被放在头文件内(以便不同源码含入),因此有必要将指针(非指针指向之物)声明为const。若要定义一个常量的char*-based字符串,则有:
const char* const authorName="Scott Meyers";
或 const std::string authorName("Scott Meyers");
2.class专属常量。为了将常量的作用域(scope)限制与class内,你必须让他成为class的成员(member):
class GamePlayer{private:      static const int NumTurns=5;//常量声明式      int scores[NumTurns];//使用该常量      ...};
然而所写的是NumTurns的生明式而非定义式。有些编译器需要另外提供定义式:
const int GamePlayer::NUmTurns;
同时旧式编译器也许不支持上述语法,不允许static成员在其声明式上获得初值。(此外in-class初值设定,也只允许对整数常量进行)则可将初值放入定义式中:
class CostEstimate{private:     static const double FudgeFactor;//static class 常量声明     ...//位于头文件内};const double CostEstimate::FudgeFactor=1.35;//static class常量定义,位于实现文件内
当在class编译期间需要class常量值时,可以改用“the enum hack”补偿做法,其理论基础是“一个属于枚举类型(enumerated type)的数值可权充ints被使用:
class gamePlayer{private:      enum{NumTurns=5};      int  scores[NumTurns];      ...}; 
请注意,我们无法利用#define创建一个class专属常量。
以及enum hack与#define更相似,获取一个const地址是合法的,而获取enum和#define的地址不合法。但是enum和#define不会导致非必要的内存分配。
最后就是,使用template inline函数的宏可以带来更大的效率:
template <typename T>inline void callWithMax(const T&a,const T&b){       f(a>b?a:b);}
有了consts、enums、inlines,我们对预处理器(#define)的需求降低了,但#include仍是必须品,而#ifdef/#ifndef也继续扮演控制编译的角色。
~对于单纯常量,最好以const对象或enums替换#define
~对于形似函数的宏(macros),最好改用inline替换#define

条款03:尽可能使用const

const:允许你指定一个语义约束(一个“不该被改动”的对象),编译器会强制实施者项约束。
指针中,const出现在*左边,表示被指物是常量;出现在*右边,表示指针自身是常量。
void f1(const Widget* pw);//f1、f2是相同的void f2(Widget const *pw);
STL迭代器,const声明表示,迭代器不得指向不同的东西,但所指的东西可以改变,如果希望他无法改变,就需要使用const_iterator:
std::vector<int> vec;...const std::vector<int>::iterator iter=vec.begin();*iter=10;//没问题,改变的是iter所指之物++iter;//错误,iter是conststd::vector<int>::const_iterator cIter=vec.begin();*cIter=10;//错误,*cIter是const++cIter;//没错,改变cIter
函数返回一个const值,可以防止其被客户改动:
class Rational{...};const Tarional operator* (const Rational& lhs,const Tarional& rhs);

const函数内调用non-const函数,就改动了曾经做的承诺。所以我们必须使用const_cast将*this身上的const解放掉。
const operator[]完全做掉了non-const版本该做的一切,这种情况可以将const转除:
class Textblock{public:...const char& operator[](std::size_t position)const{...return text[position];}char& operator[](std::size_t position)//调用const op[]{return    const _cast<char&>(//将op[]返回值的const转除      static_cast<const TextBlock&>(*this)[position]//为*this加上const,调用const op[]      );}...};
为了避免operator[]递归调用自己,我们必须明确指出调用的是const operator[],所以这里将*this从原始类型TextBlock&转型为const TextBlock&。
这里共有两次转型1.*this 添加const
2.const operator[]返回值中移除const


条款04:确定对象被使用前已先被初始化

通常使用C part of C++而且初始化可能招致运行期成本,那么久不保证发生初始化。一旦进入non-C parts of C++规则就有变化。这就好解释为什么array不保证其内容被初始化,而vector却需要保证。
最佳出来办法就是:永远在使用对象之前将他初始化。
int x=0;//初始化intconst char* text="A C-style string";//初始化指针double d;std::cin>>d;//以读取input stream的方式初始化
至于内置类型以外的东西,初始化责任在构造函数上,规则很简单:保证每一个构造函数都将对象的每一个成员初始化。
class PhoneNumber{...}class ABEntry{public:     ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>&phone);private:   std::string theName;   std::string theAddress;   std::list<PhoneNumber> thePhones;   int numTinesConsulted;};
ABEntry::ABEntry(cosnt std::string& name,const std::string& address,const std::list<PhoneNumber>&phones){//赋值     theName=name;     theAddress=address;     thePhones=phones;     numTimesConsulted=0;}
ABEntry::ABEntry(cosnt std::string& name,const std::string& address,const std::list<PhoneNumber>&phones)://初始化     theName(name);     theAddress(address);     thePhones(phones);     numTimesConsulted(0);{  }
不使用初始化而赋值,会使得成员对象在初始化发生时间更早,发生于这些default构造函数被调用之前,是的资源浪费。
ABEntry::ABEntry()//成员初值表     :theName(),      theAddress(),      thePhones(),      numTimesConsulted(0){}
规定总是在初值列中列出所有成员变量,以免还得记住那些成员变量可以无需初值。
当许多classes拥有多个构造函数时,每个构造函数都会有自己的成员初值列,就会导致很对重复的工作,这种情况下,可以合理的在初值列遗漏那些“赋值表现好的成员”,对他们改用赋值。

C++,有着十分固定的“成员初始化次序”(不会报错),base classes(基类)先于derived classes(派生类)。

在,问题涉及多个源码文件时,会发生使用某一non-local static对象的初始化使用了另一编译单元内的某个non-local static对象,他有可能尚未被初始化。
这时我们需要做的便是:将每个non-local static对象搬到自己的专属函数中(该对象在此函数内被声明为static)。喊句话说,non-local static 被local static替换了。
例:
class FileSystem{//来着你的程序库public:     ...    std::size_t numDisks()const;//成员函数     ...};extern Fileststem tfs;//预备给客户使用的对象class Directory{//由程序库客户建立public:    directory(params);    ...};Directory::Directory(params){    ...    std::size_t disks=tfs.numDisks();//使用tfs对象    ...}Directory tempDir(params);//为临时文件做目录
除非tfs在tempDir之前初始化,否则tempDir会使用尚未出生的tfs。
改动:
class FileStstem{...}FileSystem& tfs()//替换tfs对象{//FileSystem class 中可能是个static.    static FileSystem fs;//定义初始化一个local static对象    return fs;//返回一个reference上述对象}class Directory{...};Directory::Directory(params){    ...    std::size_t disks=tfs.numDisks();//使用tfs对象    ...}Dierctory& tempDir()//替换tempDir对象{     static Directory td;     return td;}

~为内置型对象手工初始化,C++ 不保证他们初始化。
~构造函数最好使用成员初值列,而不要再构造函数本体内使用赋值。
~为免除"跨编译单元的初始化次序"问题,用local static对象替换non-local static。



















原创粉丝点击