缘起宇轩阁

华华的技术专栏

(Effective C++)第一章 让自己习惯C++(View Yourself to C++)

1.1 条款1:视C++为一个语言联邦(View C++ as a federation of language)

C++主要的次语言(sublanguage):
l  【3-1-1】 说到底C++仍是以C为基础。区块(blocks),语句 (statements),预处理器(preproccessor),内置数据类型(built-in data types),数组(arrays),指针(pointers)等都来自于C。
l  【3-1-2】 Object-Oriented C++.类classes,封装(encapsulation),继承(inheritance),多态(polymorphism),virtual函数(动态绑定)等等
l  【3-1-3】 Template C++这是泛型编程(genericprogramming)部分,它带来了崭新的编程范型(programming paradigm),也就是所谓的template metaprogramming(TMP,模板元编程)。
l  【3-1-4】 STL是个template程序库。它对容器(containers),迭代器(iterators),算法(algorithms)以及函数对象(function objects)。

1.2 条款2:尽量使用consts,enums和inlines,少用#define (Prefer consts,enums and inlines to #define)

在C++程序中,尽量使用consts,enums和inlines,少用#define。
l  【3-2-1】对于单纯常量,最好以const对象或enum替换#defines。
l  【3-2-1】对于形似函数的宏(macros),最好改用inline函数。

例如:
const double PAI = 3.1415;
const char* const HELLO = "Hello";
const std::string AUTHOR_NAME = "wuzhenghua";
class CAnimal
{
     Private:
         const int COLOR = 1;
};
示例3-2-1 常量

我们无法利用宏#define创建一个class专属常量,因为#define并不重视作用域(scope)。
一个属于枚举类型的数值可以冒充ints使用。枚举的行为比较像#define而不像const。例如,取一个const的地址是合法的,但是取一个enum的地址就不合法,而取#define的地址也是不合法的。
#define函数一是语句引起的歧义,二是没有安全类型检查。所以最好使用inline函数,如下:  

template<typename T>
Inline  void MAX(const T& a, const T &b)
{
    f(a > b ? a:b);
}
示例3-2-2 inline函数

1.3 条款3:尽可能使用const(Use const wheneverpossible)

Const的一件奇妙事情是,它允许你指定一个语义约束(也就是指定一个“不被改动”的对象),而编译器会强制实施这项约束。

1.3.1 const与指针

面对指针,修饰指针,或修饰指针所指物。如果关键值const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示两者都是常量。

char greeting[]="Hello";
char *p = greeting;                 //non-const pointer, non-const data
const char *p = greeting;           //non-const pointer, nconst data
char * const p = greeting;          //const pointer, non-const data
const char * const p = greeting;    //const pointer, const data
示例3-3-1 const与指针变量

1.3.2 const与迭代器

如果你希望迭代器所指的东西不可被改动(即是希望STL模拟一个const T* 指针),你得使用const_iterator:   
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); // iter像T*const
*iter = 10;       //正确
iter++;           //错误
std::vector<int>::const_iterator cIter = vec.begin();// iter像const T*
*cIter = 10;       //错误
cIter++;           //正确
示例3-3-2 const与迭代器

令函数返回一个常量值,可以降低因客户错误而造成的意外,而不置于放弃安全性和高效性。
class Rational {…};
const Rational operator*(const Rational& lhs, const Rational & rhs);
Rational a,b,c;       //正确
if(a*b=c)  //其实想做一个比较而掉了一个=,编译器报错
示例3-3-3 const与operator*

1.3.3 const与成员函数

理由:第一,const是class接口比较容易被理解;第二,const使“操作const对象”成为可能。两个成员函数如果只是常量性(constness)不同,可以被重载。这是C++的一个重要特性。const成员函数不可以更改对象内任何non-static成员变量。

const与non-const成员函数应该避免代码重复。
Class TextBlock {
public:

const char & operator[](std::size_t pos) const
{
   …
}
char & operator[](std::size_t pos)
{
   return const_cast<char &>  //将op[]返回值的const去除
          (static_cast<const TextBlock &>(*this) //为*this加上const
          [pos];
}

}
示例3-3-4 const与成员函数

【注意】 non-const成员函数本来就可以对其对象做任何动作,所以在其中调用一个const成员函数并不会带来风险。

1.4 条款4:关于“将对象初始化”这事,C++似乎反复无常(Make sure that object areinitialized before they’re used)

关于“将对象初始化”这事,C++似乎反复无常。
确保每个构造函数都将对象的每一个成员初始化。但是,别混淆赋值(assignment)和初始化(initiation)。
class ABEntry{
public:
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones );
private:
    std::string theName;
    std::list<PhoneNuber> thePhones;
    int numTimesConsulted;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ){              //这些都是赋值
theName = name;
thePhones = phones;
numTimesConsulted = 0;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ): theName(name), thePhones(phones), numTimesConsulted(0)
{               //这些都是初始化

}
示例3-4-1 赋值与初始化

C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。成员初始化列(memberinitialization list)比赋值效率高,但是内置类型的效率是一样的。

一旦你已经很小心将“内置型成员变量”明确地加以初始化,而且也确保你的构造函数运用“成员初始列”初始化base class和成员 变量,那就只剩下“不同编译单元内定义之non-conststatic对象”的初始化次序。

所谓static对象,其寿命从被构造出来直到程序结束为止。这种对象包括global对象,定义于namespace作用域内的对象,在class内,在函数内,以及在文件作用域内被声明static的对象。

函数内的static对象称为local static对象,其他称为non-local static对象。

所谓编译单元(translationunit)是指产出单一目标文件(single object file)的那些源码。

C++对定义于不同编译单元内的non-local static对象的初始化次序没有明确定义。

可以解决该问题的方法:将每个non-localstatic对象搬到自己的专属函数内(该对象在此函数声明为static)。这些函数返回一个reference指向它所包含的对象。然后,用户调用这些函数,而不直接涉及这些对象。

C++保证:函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。

阅读更多
个人分类: C++语言编程
想对作者说点什么? 我来说一句

Effective C++中文版第三版 高清PDF

2018年01月03日 16.4MB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭