深度探索C++的总结

来源:互联网 发布:电脑系统解压缩软件 编辑:程序博客网 时间:2024/06/05 19:06

  --------------------------------------------------------------------------------------------------------

1.        C++是编译器幕后做了很多工作的C

基于过程的编程,算法的运用

基于对象的编程

面向对象的编程,设计模式的运用

泛型编程,标准模板库的设计实现

模板元编程

 

2.        大返回值属调用者的栈空间

函数调用时,返回值和参数如果不通过寄存器传递则一定入栈

函数返回时通过恢复ebpesp迅速退栈

注意汇编call指令,ret指令对应的下条指令地址入栈出栈操作

被调用函数入口:push  ebp;  mov ebp, esp;

被调用函数出口:mov esp, ebp;  pop ebp;  ret;

 

3.        执行期系统重要的工作

实际上也是编译幕后生成了code和一些数据信息:

多重继承下this指针的调整(注意多分支链上的同型同名虚函数)

dynamic_cast<…>,它可以在多重继承中,做不同继承链的转型

New 表达式(New 函数 + 构造函数),构造函数抛出异常时确保防止内存泄露

 

4.        (拷贝/默认)构造,拷贝赋值,析构的隐式生成

编译器如果见不到其声明则自动生成对应的public inline的声明和定义版本,注意只在被用到时才生成

默认构造,析构都是一定会插入基类调用的

拷贝构造和拷贝赋值隐式生成的会插入基类调用,显式声明定义的编译器不帮助插入基类调用

 

注意类内的常量和引用以及需要参数构造的内嵌对象需要在初始化列表里完成,因此必须通过显式的方式

 

5.        数组的运用

字符串直接量即为字符数组的特殊形式

数组不可能去接受一个指针对它的赋值,反之则可以

数组形式在参数处表达的是指针

参数是数组引用则确实表达的是数组本身,所以有界

静态区和堆栈区的数组均可以用大括号初始化,注意类对象必须是单参构造的,以及注意引起的编译期初始化和运行期初始化

多维数组的数组名表达的均是n维指针类型,而且不能改变值

数组中括号下标均在编译层面上转为指针计算

 

6.        对象构造与析构的递归顺序

构造顺序:先基类后派生类,先成员后自己

析构顺序:先派生类后基类,先成员后自己

 

7.        (虚)析构函数有声明必须有定义

没有声明析构函数可以让编译器默认生成一个public inline的声明和定义版本

析构函数声明是纯虚的没有实际意义,而且其必须有定义体或不加声明以便编译器默认生成

8.        4G虚拟地址空间的布局

栈是向下增长的,即由高地址空间向低地址空间增长

 

9.        不同编译单元全局对象初始化的顺序无法保障

因此尽量把全局对象放在函数内静态对象(局部静态对象),并使用get函数访问之

 

10.    函数内静态对象初始化在函数首次调用之前

一般大的类的对象,编译器通过生成一个bool标志来控制其构造函数的调用情况,只允许调用一次在第一次调用该函数时或之前

全局的对象在main函数之前完成

 

11.    编译静态性,根据对象静态型别推断其内存布局

动态性需要借助虚函数表,塞入一些运行时数据信息和产生一些幕后的code,以便typeiddynamic_cast使用

 

12.    虚函数连接在对象构造处,其调用处遵循偏移查表机制

这就是不同二进制模块中一方只使用接口类可以通过编译连接的本质原因

 

13.    依赖对象地址加偏移地址访问类内非静态成员

即使碰到内联也是一回事

 

14.    所有静态对象和全局对象均安置在静态区

注意区分编译期初始化和运行期初始化

 

15.    常量与引用必须初始化(非常量引用不得引用临时对象和常数)

类内的初始化在成员初值列中进行

 

16.    内联是对编译器的一种推荐

Debug模式下编译连接不会发生内联

虚函数内联往往无效,最原始基类的析构函数容易内联成功

显示指明类名的虚函数调用容易内联成功

 

17.    字符串与浮点常数的编译期处理

一般会安置在静态区

 

18.    模板值参必须是编译期常数

只要是编译器常量表达式也是可以的

 

19.    编译器推断规则(选择非模板函数的推断能力强,模板具现上的推断能力减弱)

 

20.    转型的类别(显式与隐式)

模板的运用中,对于隐式的推断能力下降

 

21.    访问权限只在编译层面上有效

访问类内成员的权限

派生某个类的权限

类的对象的拷贝权限

 

22.    名称查找(针对类模板能力减弱,需要强制表达)(先查找后可取性分析)

从内部作用域往外部查找

从派生类往基类查找

查找的时候遵循明显的覆盖规则

 

23.    名称冲突:覆盖与重载,遵循作用域规则

 

24.    须强制表达以解决二义性冲突

原因是如果编译器采用默认行为会导致与客户理解的不一致

这种冲突难以发现,而且麻烦

 

25.    区分编译期初始化(编译期常数,大括号初始化)和运行期初始化(全局对象,静态对象)

 

26.    函数模板

与类模板成员函数一起,这是编译期多态的基石,当然还需要重载的配合

 

27.    类模板以及类模板的成员函数

 

28.    成员模板(类成员是函数的模板,类成员是类的模板)(泛化拷贝构造,和泛化拷贝赋值)

 

29.    模板的特化与部分特化

模板函数没有部分特化,但是有重载

模板类有部分特化,其实就像是类的重载

特化类模板成员函数定义前面不需要加上template<>

特化模板函数定义前面需要加上template<>

 

30.    模板元编程是图灵完全的

图灵完全即足够强大可以像图灵机一样计算一切

 

31.    成员指针,函数指针以及模板函数指针

数据成员指针保存的是到类对象地址的偏移值

非虚函数成员指针即是函数地址

虚函数成员指针其实只是一个索引值

函数指针作为参数以及返回值,因为表达麻烦怪异,建议使用typedef完成

 

32.    编译期多态(重载,模板函数)与运行期多态(虚函数)

编译器多态:预编译和编译时刻的自动筛选能力

运行期多态:运行时刻借助虚函数表的自动筛选能力

 

33.    继承的明细分类

l  接口继承,实现继承

纯虚函数:接口继承,可能有实现继承

虚函数:接口继承,实现继承

成员函数:实现继承

 

l  数据继承,功能继承

 

l  单继承,多继承

 

l  虚拟继承,非虚拟继承

   

34.    代码与数据一定是分离的,对于delete this不要感到奇怪,不管一个对象在哪里,在哪个函数那里被删除,考虑它的成员数据在删除对象之后是否被读写很重要。如果存有,就有问题,行为未定义。

 

35.    通常出现在头文件中的const int i = 100;可知,一般i并不占用存贮空间,因为编译器在编译的时候对i进行替换成立即数100

原创粉丝点击