[Effective C++]

本文介绍了C++编程的一些关键原则,包括使用explicit防止隐式类型转换,理解构造函数与赋值操作的差异,以及如何在初始化、const、enum、inline的使用上遵循最佳实践。此外,还强调了const在函数返回值和成员函数中的应用,以及对象初始化的重要性。通过示例展示了如何避免潜在错误并提高代码质量。

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

1、除非有一个非常好的理由允许构造函数被用于隐式类型转换,否则应该把它声明为explicit

2、如果一个新对象被定义,那一定有个构造函数被调用,不可能调用赋值操作。如果没有新对象被定义,就不会有构造函数被调用,那么就是赋值操作被调用

Widget w1;          //调用默认构造
Widget w2(w1);      //调用拷贝构造
w1 = w2;            //调用赋值
Widget w3 = w2;     //调用拷贝构造

第一章 让自己习惯C++

C++是个多重泛型编程语言(multiparadigm programming language)、一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。

条款1:将C++看作四个次语言的联邦:

1、C。C++中的区块、语句、预处理器、内置数据类型、数组、指针都是来自C。

2、Object-Oriented C++。类、封装、继承、多态、虚函数等。

3、Template C++。泛型编程

4、STL。

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

为何用const、enum替代#define?

1、宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查。

2、#define不能用来定义class专属常量,也不能提供任何封装性,不存在private #define,const可以封装。而const可以用于类成员变量的定义。当为让类共享同一个常量,并且只有一份,在前面加static关键字修饰。但是C++只允许对static的整数常量进行in-class处置设定,即

class CostEstimate{

private:
    static const double m_dou;    //在头文件内声明
    static const int m_int = 20;  //允许在头文件内初始化static const int类型
    //若编译器不允许在头文件内初始化static const int 则使用enum替代
    enum {NumSize = 20};    //当然enum只能设置整型
    int scores[NumSize];    //类内能初始化static const int也是为了能初始化数组大小  

}
//其他的static const类型需要在实现文件内设初值
const double CostEstimate::m_dou = 1.35;    

3、enum更像#define,因为enum是无法取地址的。

为何用inline替代#define?

#define EXAMPLE(X) (X*X)
int a = 1;
EXAMPLE(a++);    //得到2
EXAMPLE(a+a);    //得到15

显然,上述例子得到的结果并非我们想要的,但是如果宏定义的使用不注意的话,就容易造成上述问题,且#define不可以调试,inline可以调试。且inline效率跟#define相同。

条款3:尽可能使用const

int a = 20;
int * const pa1 = &a;    //指针常量,指针只能指向初值,不能更改
const int * pa2 = &a;    //常量指针,指针指向的值不能更改
int const * pa3 = &a;    //常量指针

STL中存在一种迭代器,所指的东西不能被改动,即const_iterator用法跟iterator相同

令函数返回值为一个常量,可以降低很多以外的错误,如:

class Rational1{

    int nums;
public:
    const friend Rational1 operator*(const Rational1&a, const Rational1& b);
};


const Rational1 operator*(const Rational1&a, const Rational1& b){
    Rational1 res;
    res.nums = a.nums*b.nums;
    return res;
}

int main(){
    Rational1 a,b,c;
    (a*b) = c;        //报错,通过const可以避免这种操作发生
}

C++中const对象只能调用const成员函数,而非const对象都可以调用。const成员函数不可以更改对象内任何non-static成员变量。mutable释放掉non-static成员变量的bitwise constness约束。

C++的const成员函数遵循bitwise constness约束,即const成员函数不会修改对象内的任何数据,而使用mutable修饰的成员可以被const成员函数修改。const成员函数只能调用const成员函数。

对于类中的const和非const成员函数,如果名字相同,且功能一致的话,为了避免代码重复,可以使用non-const调用const来实现non-const函数:

class TextBook{
public :
    const char& operator[](size_t position) const{
        return text[position];
    }
    char& operator[](size_t position){
        return  const_cast<char&>(static_cast<const TextBook&>
                                            (*this)[position]);
    }
}

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

C++中对于类的构造函数初始化所有成员变量,最好使用构造函数的初始化列表进行初始化,如果在构造函数体内通过赋值操作对成员变量进行赋值,在进入构造函数前会先调用默认构造函数进行初始化一遍,因此效率会比直接上初始化列表低一些。且C++中初始化列表无论怎么改变初始化的成员变量的初始化顺序,其初始化顺序都是按类中声明的顺序进行初始化的。

class Rational{
    int nums;
    double prices;
    std::string str; 
public:
    Rational(int nums, double price, std::string str):nums(nums),prices(price),str(str) {}
};

C++将函数中的声明static变量称为local static变量,将非函数中的声明static变量成为non-local static变量。

对于在多个文件中的non-local static变量,如果一个non-local static变量的初始化需要使用另一个文件中的non-local static变量,但是编译器无法检测出其没有初始化。对于这种问题,可以通过将每个non-local static对象搬到自己的专属函数中,对象在函数内被声明为static。而函数返回指向对象的引用。用户直接调用这些函数,而不直接使用这些对象。

class FileSystem{};
FileSystem& tfs(){
    static FileSystem fs;
    return fs;
}

class Directory{};
Directory::Directory(){
    size_t disk = tfs().numDisk();
}

Directory& dir(){
    static Directory dir;
    return dis;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值