读书笔记之:Exceptional C++ Style (2004) [++]

1. vector中元素的访问:[]与at操作

2. 调整vector的大小:reserve

reserve是保证vector的容量至少为某个值,它不会减少vector的容量。

resize是改变vector的大小,这个操作比较严格,给出多少,结果vector的容量就变为多少。

3. sprint的替代方案

4. 定位new表达式

5. 模板特化与函数重载

类模板可以被偏特化或者全特化,函数模板则只能够被全特化

重载决议只决定选出主模板

分析下面的代码:

#include <iostream>
using namespace std;
template <class T>
void f(T){
    cout<<"f(T)"<<endl;
}
template <class T>
void f(T*){
    cout<<"f(T*)"<<endl;
}
template<>
void f<int>(int*){
    cout<<"f<int>(int*)"<<endl;
}
template<>
void f<int*>(int*){
    cout<<"f<int*>(int*)"<<endl;
}                              
int main(){
    int *p;
    f(p);
}

程序运行结果是:f<int>(int*)

即调用的是第3个函数。

这儿容易让人疑惑的是,为什么不是第4个函数:template<>void f<int*>(int*) 呢?这个是template <class T>void f(T)的模板特化啊。

要理解这个就需要注意一点:带有函数模板的重载进行决议的时候,选择的规则是首先选择最优的普通函数,如果没有适合的普通函数那么就要从模板函数中选择。而进行选择的时候,是决定选择的哪个主模板函数,然后再看这个主模板函数是否存在对应的特化。所以,对于上面的选择,首先是在

template <class T>

void f(T)

template <class T>

void f(T*)

之间进行选择,发现第2个更加合适,这样就确定了主模板函数,然后在查找该主模板函数是否有对应的特化函数,这样正好找到了

template<>

void f<int>(int*)

它是对int的特化,所以最后选择了这个函数。

 

分开看是如下:

 

6. 异常使用的正确位置

资源获取即初始化RAII

尽量通过析构函数来进行异常环境下的自动清理工作,而不是通过try/catch块

永远不要允许析构函数、释放操作(deallocation)以及swap()函数抛出任何异常,因为否则的话,就没法安全且可靠地进行资源的清理了。

 

7. 违反异常规格

C++中的一些比较不起眼的特性,逐渐被尘封在语言的角落,直到许多人甚至于忘记了它们的存在。这正是为什么你看到的关于它们的文章总是相对较少,譬如像valarray、bitset、locale以及实际上是合法的表达式"5[a]"这些冷僻的特性。而对于异常规格来说同样如此。

如果函数违反了异常规格的话,该函数肯定不能够以通常的函数返回方式返回,而它做的事情有如下两件:

8. 类继承中构造顺序

 

验证程序如下:

#include <iostream>
using namespace std;
class B1{
    public:
    B1(){ cout<<"Constructor:B1()"<<endl; }
};
class V1:public B1{
    public:
    V1(){ cout<<"Constructor:V1()"<<endl; }
};
class D1:virtual public V1{
    public:
    D1(){ cout<<"Constructor:D1()"<<endl; }
};

class B2{
    public:
    B2(){ cout<<"Constructor:B2()"<<endl; }
};
class B3{
    public:
    B3(){ cout<<"Constructor:B3()"<<endl; }
};
class V2:public B1,public B2{
    public:
    V2(){ cout<<"Constructor:V2()"<<endl; }
};
class D2:virtual public V2,public B3{
    public:
    D2(){ cout<<"Constructor:D2()"<<endl; }
};
class M1{
    public:
    M1(){ cout<<"Constructor:M1()"<<endl; }
};
class M2{
    public:
    M2(){ cout<<"Constructor:M2()"<<endl; }
};
class X:public D1,public D2{
    public:
    X(){ cout<<"Constructor:X()"<<endl; }                     
    M1 m1_;
    M2 m2_;
};
int main(){
    X x;
}

9. 访问权限的使用

 

10. 类中隐式声明的函数

11. C++中不同层次的内存分配情况

12. new或malloc实际分配空间的大小

如果想到内存对齐的情况,这个问题就很好解决了。

内存对齐的情况要求实际得到的内存肯定不会小于要求分配的。一般都是高于这个值。

 

13. STL中各容器额外的内存开销

 

14. C++中new三种形式

 

15. 类相关的new:与全局new是不同的

简单new:plain new的重载

定位new:placement new的重载

nothrow new的重载

16. 初始化陷阱

注意在初始化的时候,不要搞成函数声明

 

17. C++操作符上的贪婪匹配

18. C++中的union

 

19. 单片式设计陷阱

 

 

原文地址:https://www.cnblogs.com/xkfz007/p/2645568.html