美文网首页Effective C++我爱编程
【Effective C++(1)】让自己习惯C++

【Effective C++(1)】让自己习惯C++

作者: downdemo | 来源:发表于2017-12-22 11:33 被阅读19次

01 视C++为一个语言联邦

  • 一开始C++只是C加上OOP特性,但随着C++成熟就不再只是C with classes,如今C++是一个多重范型编程语言,同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式,因此应当将C++视为一个由相关语言组成的联邦而非单一语言,在其某个次语言中,各种守则倾向简单易懂
  • C++有四个次语言
    • C:C++的基础,区块、语句、预处理器、内置类型、数组、指针都来源于C,C的局限在于没有模板、没有异常、没有重载......
    • Object-Oriented C++:C with Classes所诉求的部分,主要涉及概念有类(构造函数、析构函数)、封装、继承、多态、虚函数(动态绑定)......
    • Template C++:C++的泛型编程部分,大多数程序员经验最少的部分,它带来了崭新的编程范性,即template metaprogramming(TMP模板元编程)
    • STL:STL是个template程序库,对容器、迭代器、算法、函数对象的规约有极佳的紧密配合与协调

02 尽量以const,enum,inline替换#define

#define ASPECT_RATIO 1.653
  • 编译器看不到ASPECT_RATIO,#define在预处理期间替换字符,此时如果用此常量获得一个错误信息,提到的是1.653而非ASPECT_RATIO,你会不知道这个1.653是怎么来的从而浪费时间去追踪它,改用const就不会出现这样的情况
const double Aspectratio = 1.653; // 大写名称通常用于宏,这里改写法
  • 以常量替换#define时有两种情况,一是定义const pointer,常量定义式通常放在头文件里,所以有必要将指针(而不是指针所指之物)声明为const,例如定义一个char*字符串必须const两次
const char* const authorName = “Scott”;
  • 不过string对象通常比char*合适,所以上述定义这样写更好
const std::string authorName("Scott");
  • 第二种情况是class中的常量,为确保此常量至多只有一个实体,必须让它成为static成员
class Gameplayer {
private:
    static const int NumTurns = 5; // 常量声明式
    int scores[NumTurns]; // 使用此常量
    ...
};
// 上述是声明式而非定义式,如果编译器要看到一个定义式,这样写
const int Gameplayer::NumTurns; // 不用赋值,因为声明时已经有了初值

// 如果编译器较老不允许static成员在声明式上获得初值,可以将初值放在定义式
class CostEstimate {
private:
    static const double FudgeFactor; // 常量声明位于头文件内
    ```
};
const double CostEstima::FudgeFactor = 1.35; // 位于实现文件内
  • 如果编译期间需要class常量值,如上述数组声明式中,可以用"the enum hack"补偿做法
class GamePlayer {
private:
    enum { NumTurns = 5; }
    int scores[NumTurns];
    ...
};
  • 取enum地址是不合法的,如果不想让别人获得一个pointer或reference指向你的某个int常量,enum可以实现此约束。enum被许多代码用到,"enum hack"是TMP的基础技术
  • 宏看起来像函数而没有函数的额外开销,但容易造成奇怪的错误
// 对a和b的较大值调用f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a累加2次
CALL_WITH_MAX(++a, b + 10); // a累加1次
  • 所以不必纠结于#define,用template inline函数可以同时获得宏的效率和一般函数的可预料行为及类型安全性
template <typename T>
inline void callWithMax(const T& a, const T& b)
{
    f(a > b ? a : b);
}
  • const、enum、inline可以降低对预处理器的需求,但#include仍然是必需品,#ifdef/ifndef也扮演着控制编译的重要角色。对于单纯常量,用const或enum替换#define,对形似函数的宏则用inline替换

03 尽可能使用const

  • 这两种形式都有人用,意义相同
void f1(const Widget* pw);
void f2(Widget const* pw);
  • 如果希望迭代器类似于const pointer则声明为const,如果希望迭代器类似pointer to const则使用const_iterator
std::vector<int> v{1, 2};
const std::vector<int>::iterator it = v.begin();
*it = 10; // 正确
++it; // 错误
std::vector<int>::const_iterator it2 = v.begin(); // 相当于v.begin()
*it2 = 10; // 错误
++it2; // 正确
  • const最大的作用是函数声明返回const,这样可以减少因客户错误造成的意外,而又不至于放弃安全性和高效性,比如有理数的operator*声明式
class Rational { ... };
const Rational operator*(const Rational& lhs, const Rational& rhs);
// 为什么要用const,往下看
Rational a, b, c;
...
(a*b) = c;
// 在a * b的结果上再赋值,虽然是很明显的错但很容易无意识造成,例如
// if(a * b = c)
  • 函数形参为pointer to const或reference to const也能造成重载,在类中,把const写在函数声明后来表示不允许该函数修改变量,如果想在const成员函数中修改成员变量,把成员变量声明为mutable
class CTextBlock {
public:
    ...
    std::size_t length() const;
private:
    char* pText;
    mutable std::size_t textLength;
    mutable bool LengthIsValid;
};
std::size_t CTextBlock::length() const
{
    if (!lengthIsValid) {
        textLength = std::strlen(pText);
        lengthValid = true;
    }
    return textLength;
}
  • 在const和non-const成员函数中避免代码重复,做法是先写const版本,然后在non-const版本中对其调用,返回类型再用const_cast去const
// 注意形参类型是指针或引用,const才构成重载
const string& shorterString(const string& s1, const string& s2)
{
    return s1.size() <= s2.size() ? s1 : s2; // return的是常量
    // 注意,不要返回局部对象的引用或指针!
}
string& shorterString(string &s1, string &s2)
{
    auto& r = shorterString(const_cast<const string&>(s1),
                            const_cast<const string&>(s2));
    return const_cast<string&>(r); 
}
shorterString(s1, s2);
// 如果s1或s2中至少有一个常量,则只调用第一个函数
// 如果都是非常量则调用第二个,第二个再调用第一个
// 先把非常量转为常量形参,再把返回的常量转为非常量

04 确定对象被使用前已被初始化

  • 在使用对象之前将其初始化,对于内置类型手动完成,对于其他初始化责任落在构造函数身上
  • 不要混淆赋值和初始化
class A{
public:
    A(const std::string& s, const std::list<int> n);
private: 
    std::string name;
    std::list<int> number;
    int x;
};
A::A(const std::string& s, const std::list<int> l)
{
    name = s; // 这些都是赋值而非初始化
    number = n;
    x = 0;
}
  • C++规定,初始化发生在进入构造函数体之前,上述只是赋值,初始化发生在默认构造函数被自动调用之时,但对内置类型x来说并不保证在赋值前获得初值。构造函数较好的写法是用成员初值列,这样省去了调用默认构造函数的过程,效率更高。对内置类型来说,初始化和赋值的成本相同,但为了一致性最好也通过成员初值列来初始化
A::A(const std::string& s, const std::list<int> l)
   :name(s),
    number(n),
    x(0)
{}
  • 如果想通过default构造成员变量也可以用成员初值列
A::A()
   :name(),
    number(),
    x(0)
{}
  • static对象包括global对象、namespace内的对象、类,函数,file作用域内被声明为static的对象,函数内的static称为local static对象(因为它们对函数而言是local),其他static称为non-local static对象,static对象在main函数结束时销毁
  • 编译单元:产出单一目标文件的源码
  • 成员初始化次序是固定的,但对不同编译单元的non-local static对象初始化次序没有明确定义,如一个编译单元的某个non-local static对象初始化用了另一编译单元的某个non-local static对象,此时后者可能还未初始化。解决此问题的一个小设计是,将每个non-local static对象写到一个inline函数内,该对象在此函数内声明为static,函数返回一个指向该对象的引用,用户调用这些函数而不直接指涉这些对象,这是Singleton模式的一个常见实现手法,但从另一角度看,这些函数内含static对象会使它们在多线程中带有不确定性
class Singleton {
public:
    static Singleton& Instance()
    {
        static Singleton instance;
        return instance;
    }
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
};

相关文章

  • 要读的书书名

    1, 重读《Effective C++》《 More Effective C++》《by Scott Meye...

  • 2018-09-16

    Effective c++第三版 让自己习惯C++ 条款01:视C++为一个语言联邦 C++是一个同时支持过程形式...

  • 【Effective C++(1)】让自己习惯C++

    01 视C++为一个语言联邦 一开始C++只是C加上OOP特性,但随着C++成熟就不再只是C with class...

  • Effective C++ 1: 让自己 习惯 C++

    part1 让自己 习惯 C++ 1 视 C++ 为 1个 语言联邦 2 尽量以 const / enum / i...

  • 《Effective C++》的做法原理剖析

    在本篇文章中,会去写一些小实验,以实现Effective C++中提到的一个原则。 让自己习惯C++ 1.视C++...

  • Effective C++ 让自己习惯C++

    1.C++可视为一个由相关语言组成的联邦,而不是单一语言。每一个次语言都有自己的规约,编程守则也视情况而变化。 C...

  • C++ ---- 条款

    摘抄自Effective C++ 改善程序与设计的55个具体做法(第三版 中文版) 让自己习惯C++ 条款01:视...

  • 任务列表

    C++ 《C++ primer》、《STL源码解析》、《effective C++》、《深度搜索c++对象模型》 ...

  • (Boolan) Week1

    1.前言 c++是由 c++语言和标准库两部分组成。 推荐书籍: Effective c++ The c++ st...

  • C++笔记

    C++相关书籍:C++ primer,c++标准程序库,Effective C++(Scott Meyers),C...

网友评论

    本文标题:【Effective C++(1)】让自己习惯C++

    本文链接:https://www.haomeiwen.com/subject/iletgxtx.html