让自己习惯C++(第一部分)(条款1 ~ 条款4)

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

C++的四个次语言部分:C, Object-oriented c++, template c++, STL

请记住:

C++ 高效编程守则视情况而变化,取决于你使用 C++ 的那个部分。

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

练习代码

// 含有数组元素的类
class ArrData {
public:
    ArrData() = default;

    ArrData(int ip, int jp) : i(ip), j(jp), arr{1, 2, 3}, vec(3, 0) {}

private:
    int i;
    int j;
    int arr[5];
    vector<int> vec;
//    static int snum = 0; // 错误,必须在类外定义,类里面声明
    static int snum; // 仅声明,right
    static const int scnum = 0; // right,仅有const整型static成员可以在类里面定义
    const int cnum = 0; // right
    int num = 0; // right
//    static double sfactor = 0.0; // 错误,非整形的static成员均不能再类里面定义
    static double sfactor; // 仅声明,right
//    static const double scfactor = 0.0; // 错误,非整形的static成员均不能再类里面定义
    static const double scfactor; // 仅声明,right
    const double cfactor = 0.0; // right
    double factor = 0.0; // right
};

int ArrData::snum = 0;
double ArrData::sfactor = 0.0;
const double ArrData::scfactor = 0.0;

请记住:

对于单纯常量,最好以const对象或者enums替换#define

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

条款3:尽可能使用const

理解几种const的形式的意义

        const Widget * pw; // pw指向内容不可修改
        Widget const * pw; // pw指向内容不可修改
        Widget * const pw; // pw指针不可修改,即不能指向其他内容
        const Widget * const pw; // pw指向内容不可修改,且pw指针不可修改,即不能指向其他内容
        
        vector<int> vec{1,2,3,4};
        const auto iter = vec.begin(); // const iter, iter不可修改
        ++iter; // 错误,iter迭代器不可修改,类似于指针不可修改
        auto citer = vec.cbegin(); // citer迭代器指向内容不可修改

const 对象只能调用const成员函数;

非const 对象可以调用任何成员函数;

const 成员函数可以访问任何成员变量,只要不修改即可(其实也可以修改,下面详述);

标准库中容器部分接口(不修改容器内容的比如empty(), size(),以及部分会修改内容的比如operator[], at()等)都提供了const 和non-const 两个版本的接口,这是C++的一个重要特性,两个函数如果只是常量性不同,可以被重载

bitwise const: 成员函数只有在不修改任何成员变量时才可以说是const

logical const:  const成员函数可以修改部分bits,但是只有在客户端侦测不出的情况下,可以用mutable实现。

可变数据成员,mutable, 使const成员函数也可以访问且修改其值;

const 和 non-const成员函数中避免重复:

1)可以使non-const 版本调用 const 版本,注意调用中的类型转换;

2)不应该使const 版本调用 调用non-const版本,这破坏了const不改变成员的规则;

    // const版本[]
    const char& operator[](size_t position) const {
        ....
        return data[position];
    }
    // non-const版本[]
    char& operator[](size_t position) {
        ....
        // 注意,先转换为const object调用const版本,否则将陷入non-const的无穷无尽递归调用中
        // 再调用const_cast去掉返回引用的const属性
        return const_cast<char&>(static_cast<const Widget &>(*this)[position]); 
    }

请记住:

将某些东西声明为const可以帮助编译器检测错误用法,const 可用于任何作用域内的对象,函数参数,函数返回类型,成员函数等;

编译器强制实施bitwise const检测,但是你的代码应该根据业务施行logical const;

当const 和non-const成员函数有实质等价的实现时,令non-const版本调用const版本可避免代码重复;

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

C中的数组不保证其内容被初始化,而C++中的vector容器却有此保证,对象调用默认构造函数,内置类型采用值初始化;

        vector<int> vec(5);  // 默认值初始化为0
        vector<double> vec2(4); // 默认值初始化为0.0

成员变量的初值是由文件或者数据库读入等类似场景,应该抽取一个Init函数完成该功能,供所有构造函数调用,不应该在初值列中初始化

static对象,其寿命从被构造出来直到程序结束为止,在函数内的对象称为local static对象,其余称为non-local static对象,包括:

1)global对象;

2)定义于namespace作用域内的对象;

3)在类里,函数里,文件作用域里声明为static的对象;

C++对定于与不同编译单元内的non-local static对象的初始化相对顺序并无明确定义。因为这是相当困难甚至无法完成的。

请记住:

为内置类型对象进行手工初始化,因为C++不保证初始化它们;

构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作,初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同(这点其实不一定,不同也没关系,但是要保证初值列都采用参数,不要让成员的初始化使用另一个成员,有前后以来关系);

为免除“跨编译单元的初始化次序”问题,请以 local static 对象替换 non-local static 对象

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112427122
今日推荐