item 25:Consider supprot for a non-throwing swap
考虑写出一个不抛异常的swap函数swap作为STL的一部分,也是异常安全性能编程的脊柱以及用来处理自我赋值的常见机制。
缺省swap(std::swap)我们先来看看缺省版本的swap吧:123456789namespace std{ template<typename T> void swap(T& a,T& b) { T temp(a); a=b; b=temp; }}类型T需要支持copying(因为Meyers写书的时候还没有移动语义,所以书上没提,其实支持moving也是可以的,这里不作区分)。这个缺省版本有三次复制,因此在某些情况下相当鸡肋。其中最主要的是“以指针指向一个对象,内含真正数据”,即所谓的pimpl手法。1234567891011121314151617181920class WidgetImpl{public: ...private: int a,b,c; std::vector<double> v;};class Widget ...
item 2:Prefer consts,enums,and inlines to
用consts、enums、inlines替代#definedefine是一个预处理指令,表示一个宏,它不被视为语言的一部分,这正是问题所在。
#define的问题1#define ASPECT_RATIO 1.653
我们要知道,记号ASPECT_RATIO可能未被编译器看到,或者在编译器处理源码之前就被预处理器移走,于是,该记号没有进入记号表(symbol table)。于是导致莫名奇妙的编译错误,错误信息可能会提示1.653,如果这个定义在别人的头文件中,那你是很难追踪的。当然,还可能出现在记号式调试器中,也是因为没有进入到记号表中。
用const代替#define解决上述问题的常见方法:1const double AspectRatio=1.653;作为语言常量,AspectRatio是肯定被编译器看到的。
const的特殊情况1.常量指针常量定义式通常放在头文件中①,因此有必要将指针声明为const。常量的char-based字符串1const char* const authorname="Scott Meyers";这个如何解读?写成`char con ...
item 1:View C++ as a federation of language
视C++为一个语言联邦C++最早只是C with Classes,C++经过不断的发展,Exception、template、STL,今日的C++已经是个多重范型编程语言,支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional、泛型形式(generic)、元编程形式(meta programming)。我们将C++看成一个语言联邦而不是单一语言,C++有如下四个次语言:
C
Object-Oriented C++
Template C++
STL
C++并不是带有一组守则的一体语言,而是每个次语言都有着自己的规约,而构成的联邦政府。
item 10:Have assignment operators return a reference to *this
令operator=返回一个reference to *this赋值运算符重载一般都是返回一个对*this的引用,这与资源管理优化及内置类型的要求是分不开的。
连锁赋值12int x,y,z;x=y=z=15; //连锁赋值
连锁赋值是采用右结合律的,即1x=(y=(z=15));为了实现连锁赋值实际上并不要求必须返回一个引用,我们返回值也是可行的。但是不是一个很好的主意。我们看下面这个例子:1234567891011121314151617181920212223242526272829303132333435#include <iostream>using namespace std;class A{private: int val;public: A(int a=0):val(a){} A(const A &a):val(a.val) { cout<<"constructor"<<endl; } A operator= ...
item 7:Declare destructors virtual in polymorphic base classes
多态基类应当声明virtual析构函数当继承体系中涉及动态分配对象时,设置一个基类virtual析构函数就显得很必要了。因为多态,我们在调用虚函数时根据绑定的真实类型选择合适的版本。调用非虚函数,则直接根据静态类型进行绑定,不能令动态类型和静态类型不一致(后面补充了一些细节)。再回到这上面来,析构函数是non-virtual,但我们用的是一个基类指针指向一个派生类对象,那么调用时就会调用基类的析构函数,那么派生类部分的析构呢?派生类部分并不会被析构,从而造成“局部销毁”现象,这很可能会导致内存泄漏以及给调试器添堵。所以你要知道C++编译器有明确要求:
当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果是未定义的。
要完整的删除基类指针指向的动态分配的派生类对象,只要将基类的析构函数声明为virtual即可,此时就会动态绑定派生类对象的析构函数,并沿着继承体系隐式调用基类部分的析构函数,直至完整销毁。
不含虚函数的类不需要virtual析构函数一个类如果不含虚函数,那么一般意味着它不是被用做一个基 ...
存储类说明符
Storage class specifiers一个名字除了拥有它的作用域,还有两个重要性质:storage duration和linkage。
说明符种类存储类说明符可分为:
auto
register
static
extern
thread_local
mutable
auto C++11已经改变用法了(类型判断)。registerC++17已废除,线程暂不讨论(暂时还没了解(′д` )…彡…彡)。mutable对于存储期和链接属性都是无影响的。其作用本来也不是体现在这里。因此我们只讨论两个修饰符:
specifier
storage duration
linkage
static
static or thread
internal
extern
static or thread
external
storage duration把线程的存储期去掉,有三种存储期:
automatic
static
dynamic
automatic storage duration
automatic storage duration. The stora ...
C与C++的作用域
Scope Region作用域这个概念不是C++特有的,而是由C继承过来的,但同时添加了C++专属的新作用域,我们先介绍C的作用域再引入C++的作用域。
Translation units在介绍C的作用域之前,要先了解什么叫做翻译单元(亦称编译单元:compilation units)。
A C program consists of units called source files (or preprocessing files), which, in addition to source code, includes directives for the C preprocessor. A translation unit is the output of the C preprocessor – a source file after it has been preprocessed.
正如我们知道的,C(C++)程序是由多个源文件组成的,而源文件包括源代码和预处理指令,如#include指令和#define指令,而翻译单元正是一个源文件经过预处理器合并预处理指令和源代 ...
非限定型查找
Unqualified name lookup
Name lookup stops in the scope in which a name is found and it finds all declarations in that scope matching that name. It does not continue to look in enclosing scopes even when there could be better matching functions located there. Then there’s also ADL, which is a different matter.
非限定型查找分两个部分:OL(ordinary lookup)和ADL(Argument dependent lookup),OL会停在寻找到名字的那个作用域,同时将那个作用域的其他匹配名字加入查找集(注意,尽管进一步查找会有更好的也不会去查了),同时根据ADL的三个触发条件:
无类成员声明
无块作用域中的函数声明(using不算)
所有声明都是函数声明
全部条件都满足 ...
模板的两段名字查找
Template的名字查找(OL and ADL)name lookup in template functionC++17 Standard said:
Such names are unbound and are looked up at the point of the template instantiation (17.6.4.1) in both the context of the template definition and the context of the point of instantiation.
模板的名字查找分两个阶段:定义阶段和实例化阶段
1.from the template definition: for non-dependent names2.during instantiation: from the definition but also, via ADL only, from the instantiation context
名字查找取决于该名字是否依赖于模板,即名字可分为dependent name 和non-depend ...
ADL
Argument dependent-name lookup
引入情景123456789101112131415161718namespace N{ class X {}; void f(X); X& operator++(X&);}int main(){ // define an object of type X N::X x; // apply f to it N::f(x); // apply operator++ to it ???}
Of course you could have written N::operator++(x), but that would have defeated the whole point of operator overloading. Therefore a solution had to be found which allowed the compiler to find operator++(X&) despite the fact ...