Skip to content
大纲

C++基础笔记

前言

面向过程和面向对象

面向过程编程POP

  • 以过程为中心的编程范式
  • 按照计算机执行的步骤,从上到下顺序设计程序

面向对象编程OOP

  • 以对象为核心的编程范式
  • 对象是类的实例,类中包括了数据的定义和对数据的操作方法

编译型语言和解释型语言

  • 编译:由编译器把整个源代码翻译成机器码,最终生成二进制文件,一次性提交给计算机执行,如C、C++。
  • 解释:由解释器将代码逐行解释成机器码,并交给计算机执行,即脚本语言,如Python、JavaScript。

进入C++

C++预处理器和iostream文件

cpp
#include <iostream>
using namespace std;
/*#include <iostream>该编译指令导致预处理器
将iostream文件加入程序*/

实际上,#include编译指令将导致iostream文件的内容随源代码文件一起交给编译器,并且 #include <iostream> 指令将被iostream文件的内容替代和源代码一起组成一个复合文件

头文件

像iostream这样的文件叫做包含文件,由于它们被包含在其他文件中,也叫头文件——由于他们被包含在文件起始处。头文件使用扩展名h,C++新式风格没有h扩展名。

头文件书写有自己的规范,对于complex.h

cpp
#idndef _COMPLEX_
#define _COMPLEX_
//头文件内容
#endif

名称空间

如果使用iostream而非iostream.h,则应该使用下面的名称空间编译指令 using namespace std; 。这是using编译指令。名称空间支持是C++一大特性,这里存在一个问题:对于两个已经封装好的C++文件,它们都包含同一函数名的函数,当使用此函数时,编译器其实不知道具体指的是哪个版本的函数。名称空间使得文件内容封装在一个名称空间的单元中,这样就可以用名称空间的名称来指出具体某个文件的具体函数。

示例:

cpp
Bob::sayHi();
John::sayHi();

按照此方式来讲,类、函数、变量就是C++编译器的标准组件,它们现在都存放在名称空间std中。这意味着iostream中定义的用于输出的cout变量实际是 std::cout,而endl实际是 std::endl。而当使用using编译指令后,就可以使用std名称空间中定义的名称而不用加 std::前缀。

四种访问名称空间std的方法:

  • using namespace std;放在函数定义之前,让文件中所有函数能够使用名称空间std中所有的元素
  • using namespace std;放在特定函数定义中,让该函数能够使用名称空间std中所有元素
  • 在特定的函数中使用类似 using std::cout;这种编译指令,而不是使用 using namespace std; ,让该函数能够使用特定的元素如 cout
  • 完全不使用编译指令 using,而在需要使用名称空间std中元素时使用前缀 std::

变量名称规则

C++提倡使用一定含义的变量名,以下是命名规则:

  • 名称中只能使用字母字符、数字和下划线;
  • 名称的第一个字符不能是数字;
  • 区分大小写;
  • 不能用关键字作为名称;
  • 以两个下划线开头(如:__xxxx或者 _大写字母xxx)或者以下划线和大写字母打头的名称将被保留(留与编译器或其使用资源使用),以一个下划线开头的名称将被保留(用作全局标识符)。

常量

不可更改的变量数据即常量,在C++中有两种方式定义常量。

使用符号常量

在文件头中使用#define定义常量,也称作“宏定义”。

C++
#define ZERO 0

类似于#include#开头的语句都是预处理语句,在编译之前预处理器会查找程序中所有的ZERO,并将其替换成0。这种宏定义的方式是保留C语言特性,在C++中一般不推荐。

使用const限定符

只需要变量的数据类型前加const关键字即可。

C++
const int Zero = 0;

const修饰的对象一旦创建就不能修改,所以必须要初始化。

与使用#define定义宏常量相比,const定义的常量有详细的数据类型,而且在编译阶段进行安全检查,在运行时才完成替换,更加安全和方便。

复合数据类型

模板类Vector

vector是标准库的一部分,使用时必须要包含<vector>头文件,并使用std命名空间。

C++
#include <vector>
using namespace std;

在Vector头文件中,对vector这种类型做了定义。使用#include引入并指定命名空间std后,就可以使用vector。

基本用法

vector是C++的一个类模板,使用时必须提供具体的类型信息。

c++
vector<int> v;
  1. 初始化

    • 列表(拷贝)初始化
    C++
    vector<char> v1 = {'a','b','c'};
    vector<char> v2{'a','b','c'};
    • 直接初始化
    C++
    vector<short> v3(5);
    vector<long> v4(5,100);

基于对象

构造函数

内联函数

内联函数是C++为了提高程序运行速度所做的一项改进,一般函数和内联函数之间主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。通常,在执行一般函数的调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,然后跳到标记函数起点的内存单元,执行函数后,再跳回地址被保存的指令处(即原位置),程序将会来回跳跃地址,这需要一定的时间开销。而C++内联函数的编译代码与其他代码内联起来,编译器将使用相应函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码再跳回,因此内联函数运行速度更快,但需要更多内存。

如果要使用内联函数,必须:

  • 在函数声明前加上inline
  • 或在函数定义前加上inline

注意:内联函数不能递归;函数过大或者函数调用了自己,则编译器不会将其作为内联函数

内联与宏

inline是C++新特性,C使用预处理语句#define来提供宏——内联代码的原始实现

示例:

cpp
#define ADD(X) X+X

这并不是通过传参来实现,而是通过文本替换,X是参数的符号标记

cpp
sum_a=ADD(3);//等价于sum_a=3+3;
sum_b=ADD(3-2);//等价于sum_b=3-2+3-2;
sum_c=ADD(c--);//等价于sum_c=c--+c--;

上述代码当然可以通过添加适当的括号改进类似于 #define ADD(X) ((X)+(X)) ,但是因为宏并不能按值传递,则 ADD(c--)中的c变量依然会被调用两次递减两次,这和预期并不同,因此我们可以考虑将宏函数转换成内联函数使用。

构造函数

面向对象

贡献者

凌晨三点的修狗