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

xiaoxiao2021-02-28  120

<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是const std::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;//初始化int const 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。
转载请注明原文地址: https://www.6miu.com/read-58066.html

最新回复(0)