Effective C++ 第一章:让自己习惯C++

序言

Effective C++ 的系列博文是本人学习了《Effective C++》(第三版)之后的学习整理,大部分内容来源于原书。为了更方便地回顾和复习,所以将一些重要的知识点提取出来。分享给大家一起进步学习 ?

第一章:让自己习惯C++

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

将C++视为一个由相关语言组成的联邦而非单一的语言。可以理解为四个:

  • C:区块(block)、语句(statements)、预处理器(preprocessor)、内置数据类型、数组(array)、指针(pointers)等
  • Objective-C:类(class)、封装、继承、多态、虚函数等
  • Template C++:泛型编程
  • STL:template程序库。包含容器、迭代器、算法以及函数对象等。

条款2:尽量以const,enum,inline替换#define

让编译器替换预处理器会更好。因为#define 不被视为语言的一部分。因为所使用的名称可能并没有进入记号表(symbol table)

#define ASPECT_RATIO 1.653
//替换为
const double AspectRatio = 1.653;

define无法实现创建class专属常量,只能使用const

class Game
{
private:
    static const int NumTurns = 5;
};

有些旧式编译器不支持static成员在其声明式上获得初值。可以使用enum代替:

class GamePlayer
{
private:
   enum { NumTurns = 5; }
   int scores[NumTurns];
};

宏函数

#define CALL_WITH_MAX(a,b) f((a)>(b) ? (a): (b))

会遭遇传参等一些不可预料的行为,建议使用inline函数代替。

  • 我们并不是不再使用预处理器,而是降低需求。我们仍然需要使用#include #ifdef/#ifndef

条款3:尽可能使用const

  • const 指针问题
char greeting[] = "Hello";
const char* p = greeting;  //non-const pointer, const data
char* const p = greeting;  //const pointer, non-const data
const char* const p = greeting;  //const pointer, const data
  • 常量迭代器:迭代器所指的东西不可被改动
std::vector<int> vec;
const std::vecotr<int>::iterator iter = vec.begin();
  • const成员函数:可以知道哪个函数可以改动对象内容而哪个函数不行.
    在const函数中不可以修改对象内任何non-static成员
class Text
{
public:
     const char& operator[](int position) const
     { return text[position];}
}
  • 可修改常量mutable
class CText
{
public:
     mutable size_t textLength;  //这些成员变量即使在mutable中也可以被修改
	 size_t length() const
	 {
	 	textLength = 1;
	 }
}
  • 常量的变换
//const_cast 常量移除
const_cast<char&>(var);
//static_cast 转为const
static_cast<char&>(var);

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

数组(来自C++的C部分)不保证其内容被初始化,而vector(来自C++的STL)部分却有这个保证。

  • 构造函数
class ABEntry
{
public:
      ABEntry(const string& name, const string address);
private:
      string thename;
	  string theaddress;
};
ABEntry::ABEntry(const string& name, const string address)
{
      thename = name;
	  theaddress = address;
}

解析:会先调用默认构造函数为thename, theaddress设初值,然后立刻再对它们赋予新值。【先调用默认(default)构造函数,然后是复制操作(copy assignment)】


效率更高的写法:成员初值列

ABEntry::ABEntry(const string& name, const string address):thename(name),theaddress(address)
{ }

解析:这个版本的构造函数和上一个的最终结果相同,但通常效率较高,因为初值列中针对各个成员变量而设的实参,被拿去作为各成员变量之构造函数的实参。【只调用一次复制(copy)构造函数】


对于内置变量来说(如int)其实初始化和赋值的成本相同,但为了一致性,最好使用成员初始值来初始化。

  • 不同编译单元内定义之non-local static 对象 的初始化顺序
  1. static对象:
    函数内的static对象称为local static对象,其他static对象称为non-local static对象。

  2. 编译单元:
    单一目标文件(single object file)的那些源码。基本上是单一源码加上所含入的头文件。

  3. 如果某编译单元的某个non-local static对象的初始化动作使用了另一个编译单元内的某个non-local static对象,它所用到的这个对象可能尚未被初始化。因为C++对于定义在不同编译单元内的non-local static对象的初始化次序并无明确定义。

class FileSystem{
public:
      std::size_t numDisks() const;
};
extern FileSystem tfs;
//
class Directory{
public:
      Directory(parms);
};
Directory::Directory(parms)
{
    std::size_t disk = tfs.numDisks();  //无法确定tfs在tempDir之前先被初始化
}
Directory tempDir(parms);

解决办法:将每个non-local static对象搬到自己的专属函数内。这些函数返回一个reference指向它所含的对象。

class FileSystem{...};
FileSystem& tfs()
{
     static FileSystem fs;
	 return fs;
}
class Directory{...};
Directory::Directory(parms)
{
     std::size_t disks = tfs().numDisks();
}
Directory& tempDir()
{
     static Directory td;
	 return td;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值