1.让自己习惯C++(条款1-4)

目录

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

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

条款03:尽可能使用 const

条款04:确定对象被使用前已经被初始化


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

可以将C++的语言分为四个部分,分别是C、Object-Oriented C++、Template C++、STL。


C。说到底C++仍是以C为基础。区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据型(built-in data types)、数组(arrays)、指针(pointers)等统统来自C。许多时候C++对问题的解法其实不过就是较高级的C解法,但当你以C++内的C成分工作时,高效编程守则映照出C语言的局限:没有模板(templates),没有异常(exceptions),没有重载(overloading)……


Object-Oriented C++。这部分也就是C with Classes所诉求的:classes(包括构造函数和析构函数),封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)……等等。这一部分是面向对象设计之古典守则在C++上的最直接实施。


Template C++。这是C+的泛型编程(generic programming)部分,也是大多数程序员经验最少的部分。Template相关考虑与设计已经弥漫整个C++,良好编程守则中“惟template适用”的特殊条款并不罕见(例如条款46谈到调用template functions时如何协助类型转换)。实际上由于templates威力强大,它们带来崭新的编程范型(programming paradigm),也就是所谓的templatemetaprogramming (TMP,模板元编程)。TMP相关规则很少与C++主流编程互相影响。


STL。STL是个template程序库,看名称也知道,但它是非常特殊的一个。它对容器(containers)、迭代器(iterators)、算法(algorithms)以及函数对象(function objects)的规约有极佳的紧密配合与协调,然而templates及程序库也可以其他想法建置出来。STL有自己特殊的办事方式,当你伙同STL一起工作,你必须遵守它的规约。

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

对于下面所阐释#define的缺点部分,如果有哪里不理解的,可以尝试在下面这篇博客中的 ”宏的危险性“ 部分寻找答案

第5章 - 程序环境和预处理_小白麋鹿的博客-CSDN博客icon-default.png?t=N2N8https://blog.csdn.net/m0_73759312/article/details/128710176

1.尽量以const替换#define

在编译阶段由于宏的处理是在预处理阶段进行的,所以#define定义的宏,根本不会进入符号表,而是以直接替换的方式存在于代码中,这样做会大大降低代码的可读性,例如

#define MAX_SIZE 100

那么在代码调试时遇到的MAX_SIZE就是以100的形式出现的,那么你或者其它调试这段代码的人就会很疑惑,这个100是哪来的呢?也许他可能会猜到这是一个宏,但他很难猜测得到这个宏的信息,所以我们可以用一个常量替换上述的宏来解决这个问题:

const int MaxSize = 100; //全大写字母的方式常用于宏,所以这里更改了名称的写法

2.尽量以enum替换#define

基于数个理由enum hack值得我们认识。第一,enum hack的行为某方面说比较像#define而不像const,有时候这正是你想要的。例如取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获得一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。此外虽然优秀的编译器不会为“整数型const对象”设定另外的存储空间(除非你创建一个pointer或reference指向该对象),不够优秀的编译器却可能如此,而这可能是你不想要的。enum和#defines一样绝不会导致非必要的内存分配。
认识enum hack的第二个理由纯粹是为了实用主义。许多代码用了它,所以看到它时你必须认识它。事实.上"enum hack'"是template metaprogramming(模板元编程)的基础技术。


3.尽量以inline替换#define

对于形似函数的宏,在使用时需要特别注意而且很容易就使用出错,所以最好还是用C++的inline函数(内联函数)来替换掉宏的写法,而且还可以通过使用函数模板来实现宏不需要指定类型的特性。


有了consts、enums和inlines,我们对预处理器(特别是#define)的需求降低了,但并非完全消除。#include仍然是必需品,而#ifdef / #ifndef也继续扮演控制编译的重要角色。目前还不到预处理器全面引退的时候,但你应该明确地给予它更长更频繁的假期。

条款03:尽可能使用 const

1、“左定值右定向”。如果关键字 const 出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

2、const修饰变量,指针,函数(也就是函数返回一个const)、迭代器等,可以使程序减少错误,或者更容易检测错误,例如:

指针常量:int* const p;//指针地址不可变,指针指向值可变

常量指针:const int* p;//指针指向值不可变,指针地址可变

常量指针常量:const int* const p;//都不可变

//const 修饰迭代器

iterator - 相当于 T* const //指针常量

const_iterator - 相当于 const T* //常量指针

3、const 修饰成员函数

如果两个成员函数只是常量性不同(其他相同)则可以发生重载,其中 const 类对象调用 const 成员函数,non-const 类对象调用普通成员函数

4、使用 mutable 可以消除 non-static 成员变量的 bitwise constness 约束。

利用C++的一个与const相关的摆动场:mutable(可变的)mutable释放掉non-static成员变量的bitwise constness约束。

5、non-const成员调用const成员函数处理代码重复问题 

const成员函数和no-const成员函数可重载,即可以同时出现,在传入不同的参数时候会调用不同的版本,但是有时我们需要这样,但是又不想代码重复,我们可以在non-const成员调用const成员函数来处理这个代码重复问题。

请记住

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

条款04:确定对象被使用前已经被初始化

永远在使用对象之前先将它初始化。确保每一个构造函数都将对象的每一个成员初始化。C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。总是使用成员初值列,规定总是在初值列中列出所有成员变量。

C++有着十分固定的“成员初始化次序”。是的,次序总是相同:base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。在成员初值列中条列各个成员时,最好总是以其声明次序为次序。

简言之,对于内置类型要进行手工初始化,构造函数最好使用成员初值列表,不要在构造函数中使用赋值操作来初始化。初值列表列出的成员变量次序应该和在class中声明的次序一样,因为声明次序就是C++保证的初始化次序 对于static对象。在跨编译单元之间的初始化次序是不能确定的,因为C++只保证在本文件内使用之前一定被初始化了。

请记住

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

你可能感兴趣的:(Effective,C++读书笔记,c++,开发语言)