毕业后的C++学习笔记

                                                                                              企业C++学习笔记

      这篇博客主要分享一些我在某企业参加C++培训后的总结,里面加入了我个人的理解,经过测试的结论我都会标明。当时去参加培训的时候特别不情愿,第一堂课还给老师来了一个下马威,提了一个我自己都不明白的问题,还故意误导(有点这个倾向)一下老师的思路,结果以后老师就对我特别照顾了,学习的都是一些C++延伸的知识。在这里特别感谢培训的张**老师,给我了很多指引,让我有了自己的方向。

1.malloc与new的区别,规范为什么只允许new申请空间?

1).malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2).对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
3).new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。

2.函数指针

1).函数指针数组
     Int (*testCases[10])();//可读性差
    良好的习惯:
    typedef int (*PFV)();
    PFV testCases[10];
    备注:C的复杂指针解析是一个难题,可以参考“再再论指针”,个人觉得该文档对指针的理解很充分,让人耳目一新。
2)指向 extern “C”函数的指针
    Extern “C” void (*pf) (int);
    函数原型:extern “C” void exit(int);
    Pf = exit;
   需要注意的是:指向C函数的指针与指向C++函数的指针类型是不同的,如果在C++中,用指针指向连接指示器extern C编译的函数是会出现错误的(也有可能由于兼容的原因没有错误,这里只是说明连接指示器连接的函数需要同种语言函数的指针来指向)
 

3. C++类的sizeof(会有单独一篇介绍)

一个类有几个指向虚函数表的指针,取决于它的直接父类(含有虚函数)个数与本身是否存在虚函数。(写到这我测试了一下,发现一个类最多只会有一个虚函数)一个类如果有多个虚函数,那么它是不是有多个指向虚函数的表指针?(不是)
 

4.C++类的static

1) Static成员变量:

    类中可以定义static本类成员(不能定义一般的本类类型的成员变量),根本原因当然涉及到内存的分配时间以及内存结构:

    a. Static成员在它初始化时分配内存,分配内存顺序与初始化顺序一样(与定义顺序没有关系),这个时候类的内存结构已经确定(类的定义已经完成),static本类类型成员的内存结构自然能够确定下来。当然,对于基础类型的static成员就无所谓了,不过也是在初始化时分配内存。

    b. Static本类类型成员通常用在限制用户自定类对象的程序中(设计模式)

2) Static成员方法:

       Static成员方法是所有对象共有的,就像static成员属性(影响对象的sizeof)一样。Static成员方法不能调用普通的成员变量和方法。因为这个时候普通成员变量的内存都还没有分配(之前我认为是可以的,因为我认为函数的入口地址已经确定,但发现不行,是不是每一个对象的同一方法入口地址不同呢?但我虚函数测试了一下,发现是相同的。请大神解释一下原因)。

      而对于普通成员方法,虽然类的不同对象对于同一个方法的入口地址是一样的,但对于普通成员方法内部是否涉及到成员变量的内存分配,编译器并没有对涉及变量内存和不涉及变量内存的方法作区分,所以编译器实现时不允许static方法调用普通成员方法。从表面原因来说:类的普通成员调用时传递了this指针,只不过隐藏起来了,而static方法调用时没有this 指针,它调用普通成员方法时编译器不知道该方法到底是哪一个对象的。

 

5. inline内联函数

内联函数是怎么产生的?它出现的目的是什么?
我们假设现在要求两个数中较小的那个,我们采用min函数,但这样有一个严重的缺点:调用函数比直接计算条件操作符要慢得多。原因:
1) 必须拷贝两个实参保存到机器的寄存器;
2) 程序还必须转向一个新的位置(跳转),这是要消耗CPU时间片的(计算机组成原理)。
基于以上原因,内联函数就产生了。内联函数在编译时候在被调用点展开函数体(运行时不用跳转),从而在运行时调用min()的额外执行开销就被消除(这是一种典型的空间换时间的措施)。

使用inline函数应注意:

1) 由于inline函数是在编译期调用该函数的位置展开,不涉及到链接其他编译单元,所以必须在调用它的每个文本中定义;
2) 对于类,我们不应该将所有方法都“建议”为inline,那样编译效率就低了。
3) Inline关键字只是给编译器的一种“建议”,函数最终是否是内联函数还要看函数的具体情况。

6. friend友元

1) 友元函数的说明语句位置与访问描述无关(即publicprotectedprivate),因为友元函数本身不是类的成员。
2) 友元类通常设计为一种对数据操作或类之间传递消息的辅助类。

3)使用的友元函数可以扩大原有顶层函数的作用域,相当于把声明位置提前了,如果类的定义在该函数之前。

7. 前置声明的使用(编译相关)

Class AClass B相互引用时,要注意使用前置声明。当B.h使用#include”A.h”时,头文件A.h就不能再使用#include”B.h”,否则就会引起”循环嵌套”错误,因为A.h要的只是标识符B合法,能够被识别,所以只需在A.h中添加class B(前置声明)即可,当然需要了解class B的内存结构的A.cpp还需添加#include “B.h”。
8. 普通全局变量不允许使用的原因(编译相关)
     我们经常不允许使用普通类型的全局变量有以下两个原因:
1) 可能引起命名的冲突;
2) 编译器无法确定编译单元的编译顺序,当一个全局变量在另一个编译单元被引用时,它可能还没有被初始化,这时可能就会造成不确定的错误。

9. 模板元编程(编译相关)

类模板的定义与实现必须在同一编译单元中(一般只要.h文件),因为在编译的时候必须生成具体的函数(不带抽象类型),而.cpp文件在链接的时候才加载.h文件。

10. 让一个类不能定义对象有两种方法

1) 抽象类(纯虚函数)
2) 将它的构造函数设计为protected(private)
在某些特定的情况下,我们用virtual且函数体为空的成员函数来取代纯虚函数,这样用户就可以只需实现感兴趣的成员方法(形成特定功能的子类),这个方法经常用于生成器(结构对象模型)。    
 

11. 重载小于号(<)

当一个自定义类的对象要使用set容器来存储时,因为set会对对象就行排序,这时候你就要重载它的小于号,这个时候就要注意了,STL算法均是最严格的方式实现的(能用限定符就用了限定符),所以重载的小于号<形参必须是const类型,函数必须是const函数(set内部的less仿函数要求你必须这样做)。 

希望大家指正!

这个编辑器用得还不熟,格式排版存在很大问题。PS:今天零下6度,我写得手冷!


你可能感兴趣的:(C/C++,编译器)