Effective c++第一章(条款01-04)

本文总结了《Effective C++》第一章的主要内容,包括:将C++视为由C、Object-Oriented C++、Template C++和STL组成的语言联邦,使用const、enum、inline替换#define,尽可能使用const以提高代码安全性,以及确保对象在使用前已被正确初始化。文中解释了const的作用,避免#define带来的问题,以及初始化对象的最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从5月11日开始看effective c++,半年没看c++了,现在看居然还能看懂QAQ(我属于小白类型,连入门都算不上),把一些自己看完之后的重点总结写一下,便于今后可以回过来看看自己有什么收获。话不多说,开始今天的第一篇!!

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

让自己习惯c++

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

说实话这个主要就是让我们了解一下c++这门语言,讲的就是四点:1. C C++是以C为基础;2. Object-Oriented C++ (C with class),在原有C基础上加上面向对象的特性,封装、继承、多态…等;3. Template C++ (泛型编程);4. STL 一个特殊的template程序库,懂得都懂这东西0.0。
以上四个次语言的切换遵守编程策略

请记住:
c++ 高效编程守则视状况而变化,取决于你使用C++的哪一部分。

条款02:尽量以const、enum、inline替换#define(Prefer consts、enums、and inline to #define)

#define ASPECT_RATIO 1.653

换成

const double AspectRation =1.653;

宏定义被预处理器拿走,记号名称ASPECT_RATIO可能没有进入记号表,编译器就会发出错误,而 AspectRation为一个语言常量,肯定会被编译器看到,一定会进入记号表。对浮点常量而言,const比define导致较少量的码。

这个const的位置不同有不同含义

const char* const authorName=“ABC”;
第一个const指向"ABC",第二个指向char*指针。

再者#define为"全局"的,一旦定义,整个编译过程有效,如果需要class专属常量(只在此class中有效)需要使用const

class ConstEstimate{
private:
       static const double FudgeFactor;      //声明
       //static const int NumTurns=5;        //错误->用于数组声明时发生错误
       enum { NumTurns=5 };                  //正确
       int scores[NumTurns]
       ...
};
const double CostEstimate::FudgeFactor=1.35; //定义
//const int ConstEstimate::NumTurns=5;       //错误

这里可能会发生错误,在编译期间需要知道数组的大小,改用"the enum hack"。

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

int a=5,b=0;
CALL_WITH_MAX(++a,b);    //a被累加两次
CALL_WITH_MAX(++a,b);    //a被累加一次

a的递增次数取决与和谁比较,有点烦!!

template<typename T>
inline void callWithMax(const T& a, const T& b){
  f(a>b?a:b);
}

用inline可以同样获得宏的效率和一般函数的可预料行为和类型安全性(f函数本体不用再加括号)

请记住:
对于单纯常量,最好以const对象或enums替换#define。

对于形似函数的宏,最好以inline函数替换#define。

条款03: 尽可能使用const (Use const whenever possible)

const char* p=greeting; (char const *也可以) //const data
char* const p=greeting;                     //const pointer
const char* const p=greeting;               //const pointer,const data
std::vector<int> vec;
...
const std::vector<int>::iterator i=vec.begin();   //类似T* const
*i=10;  //成功
++i;   //错误  i为const

std::vector<int>::const_iterator cI=vec.begin();  //类似const T*
*i=10;  //错误  cI为const
++i;   //成功
class Rational {...};
const Rational operator* (...);
Rational a,b,c;
if(a*b = c)...   

编译器直接报错,避免了‘==’意外键入‘=’
当const成员函数时:

class CTextBlock{
public:
  ...
  std::size_t length() const;
private:
  char* pText;
  //std::size_t textLength;
  mutable std::size_t textLength;
  //bool lengthIsValid;
  mutable bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
  if(!lengthIsValid){
     textLength=std::strlen(pText);
     lengthIsValid=true;
  }
}

mutable 使得成员变量即使在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]);
  }
...
};

这里让non-const operator[]调用const,从而减少代码的重复量,但是不能反向做法,“让const 调用non-const”,因为const本身不能被改动。

请记住:
-将某些东西声明为const可帮助编译器侦测出错误用法。const 可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
-编译器强制实施bitwise constness,但编写程序时应该使用”概念上的常量性”
-当const和non-const成员函数有实质等价的实现时,令non-const版本调用const版本可避免代码重复量

条款04:确定对象被使用前已先被初始化(Make sure that objects are initialized before they’re used)

确保每一个构造函数都将对象的每一个成员初始化。

class PhoneNumber{...};
class ABEntry{
public:
   ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones);
private:
    std::string& name;
    std::string& address;
    std::list<PhoneNumber>& phones;
    int numTimesConsulted;      //内置型对象(int double float double...)
};
ABEntry::ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones){
   //这些是赋值而非初始化
   theName=name;
   theAddress=address;
   thePhones=phones;
   numTimesConsulted=0;
}
//使用成员初值列
 ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones)theName(name),theAddress(address),thePhones(phones),numTimeConsulted(0) {}

使用成员初值列(直接初始化)代替函数赋值,通常效率较高。使用赋值:首先还是调用default构造函数设初值,然后有对其赋值,造成default构造函数的作为浪费,而初值列可以避免此问题。对了,初值列的成员初始化次序可以不一,但最好是和成员变量的声明顺序一致(让别人也能看懂)。下面有个难点的东西:不同编译单元内定义之non-local static对象的初始化次序;

先来拆分着看:
static对象: 寿命从被构造出来直到程序结束为止(包括global对象、定义域namespace作用域内的对象、在classes内、在函数内、以及file作用域内)
non-loacl static对象: 函数内对象称为local static对象(对函数而言为local),其他static对象成为non-loacl static对象
编译单元:产出单一目标文件的源码(就是单一源码文件+所含头文件(#include files))

这里说明下,FileSystem classDirectory class 来自不同编译单元。

class FielSystem{        //自己本机建立
public:
   ...
   std::size_t numDisks() const;
   ...
};
extern FileSystem tfs;   //预留给客户使用对象
class Directory{         //有程序库客户(他人)建立
public:
   Directory();
   ...
};
Directory::Directory(){
  ...
  std::size_t disks=tfs.numDisks();   //使用你所创建的class对象
  ...
}
-----------------------------------------------
Directory tempDir();     //使用Directory

使用Directory时初始化顺序就显得重要了,tfs就必须在tempDir之前被初始化,不然tempDir的构造函数对使用为初始化的tfs.但是tfs和tempDir是在不同时间不同的使用者于不同源码文件建立,这就是不同编译单元内定义之non-local static对象,所以我们现在的目标是如何保证tfs在tempDir之前被初始化?

书上讲的太复杂,简单一点总结就是non-local static对象被local static 对象替换,采用Singleton模式(可以看看我对单例模式实现的文章:单例模式)。

class FileSystem{...};     //同前
FileSystem& tfs(){
  static FileSystem fs;    //定义并初始化一个local static对象,
  return fs;               //返回一个reference指向上述对象
}
class Directory{...};     //同前
Directory::Directory(){
  ...
  std::size_t disks=tfs().numDisks();   //tfs->tfs()
  ...
}
Directory& tempDir(){
  static Directory td;     //定义并初始化一个local static对象,
  return td;               //返回一个reference指向上述对象
}

这样使用函数返回的"指向static对象"的reference,而不再是static对象本身。

请记住:
-为内置型对象进行手工初始化,因为c++不保证初始化它们
-构造函数最好使用成员初值列,而不要在构造函数本体使用赋值操作。初值列的成员变量排列顺序应该和在class中声明的次序相同
-避免“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象

第一章至此完结,刚刚开始看可能理解的不是很透彻,希望见谅,同时大家发现错误可以指出来,大家一起学习谢谢!!

所有代码与书上一致

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值