C++ Primer Plus 学习笔记

本笔记不是全面的C++学习笔记,而是基于个人在原有的C语言基础和浅薄的C++的开发经验上的补足笔记。

本笔记仅供个人学习记录,不提供完整学习指导,如有需要请自行阅读《C++ Primer Plus》。

本文会参考 GNU C++ 进行代码调试,部分文档内容会参考 MS C++ 进行理解。

简介

C++融合了三种不同的编程方式:

  • 面向过程编程(继承于C)
  • 面向对象编程
  • 泛型编程(Generic Programming),即C++模板(Template)

过程 VS 对象

面向过程编程(Process Oriented Programming),即 结构化编程过程结构化编程,是指根据执行的操作来构思一个程序。程序任务需要解决的问题按照 “1、2、3、4” 这样的顺序一一编写,如果程序任务过大,则将较大的任务拆解成较小的可以理清结构逻辑的小任务去完成。C语言的编程开发思路就是使用程序模块(函数)来表示各个任务模块。

面向对象编程(Object Oriented Programming),简称 OOP。

结构化编程在程序逻辑的清晰度和可靠性上占据优势,但是在代码量上和面向对象相比不占优势

为了应付代码量的挑战,与强调算法的过程性编程不同,OOP强调数据。过程性编程试图使问题满足语言要求,OOP则是使语言满足问题的要求。

(Class) 是规定了 数据操作数据类型(Data Type)。类所创建的实体称为对象。

OOP 强调的是编程的数据方面,GP编程强调的是 独立于特定数据类型,即强调 通用非特定 类型。

image-20211027135117490

系统的位数

  • 系统的位数是由什么决定的?包括嵌入式系统的8位、16位、32位和通用系统的32位和64位?

位数 是由CPU的最大寻址空间决定的。

New Features in C++

以下主要从C++和C的区别点进行记录,是个人之前从未掌握过的知识,另外也会记录C++的一些高级用法。

Statement

  • C与C++的语句申明位置有哪些差异?

C语言的变量声明通常都应位于函数或过程开始位置(Pascal也是),而C++并没有该限制,因此这点可以说是C++的优点也可以是缺点。

Range-based for

C++11 引入了一种崭新的 for() 循环形式,可以逐一迭代某个给定的 区间数组集合(range, array or collection) 内的每一个元素。而其他语言可能称之为 foreach 循环,其一般性用法如下:

1
2
3
for(decl:coll){
statement
}

其中:

  • decl 是给定之 coll 集合中的每个元素的声明。
  • statement 会针对给定之 decl 进行执行

使用样例(下左)及其输出结果(下右)如:

image-20211030084723644

而且上面这种是基于 C-style array 的新旧语法搭配的 C++,实际上如果 coll 集合提供成员函数 begin()end() ,那么使用 for(decl:coll) 时便等同于使用 for(auto _pos=coll.begin(), _end=coll.end(); _pos!=_end;++_pos)

Data Type

  • 数据类型的大小由什么决定?数据类型大小(即占空内存空间)与系统位数、编译器的关联性?
  • 嵌入式系统与通用系统的数据类型大小差异在哪?
  • double 和 float 在内存上的差异?

在 C++ 中, 变量(Variable) 一词通常用于 引用 标量数据类型 的 实例,而 其他类型(class / struct)的实例通常称为 对象(Object)。

C++ 是 强类型语言 ,也是 静态类型

在代码中声明变量时,必须 显式指定其类型,或使用 关键字指示编译器从初始值表达式 auto 推断类型。 在代码中声明函数时,必须指定每个参数的类型及其返回值;如果函数未返回任何 void 值,则必须指定 。当使用允许任意类型参数的函数模板时例外。

基本(内置)类型

基本数据类型,简称 基本类型基类型

下图显示了 Microsoft C++ 实现中的内置类型的相对大小:

多个内置类型的相对大小(以字节为单位)的关系图。

下表列出了最常用的基本类型及其在 Microsoft C++ 实现中的大小:

类型 大小 评论
int 4 个字节 整数值的默认选择。
double 8 个字节 浮点值的默认选择。
bool 1 个字节 表示可为 true 或 false 的值。
char 1 个字节 用于早期 C 样式字符串或 std:: 字符串对象中无需转换为 UNICODE 的 ASCII 字符。
wchar_t 2 个字节 表示可能以 UNICODE 格式进行编码的“宽”字符值(Windows 上为 UTF-16,其他操作系统上可能不同)。 这是用于 std::wstring 类型字符串的字符类型。
unsigned char 1 个字节 C++ 没有内置字节类型。 使用 unsigned char 表示字节值。
unsigned int 4 个字节 位标志的默认选项。
long long 8 个字节 表示非常大的整数值。

类型数据的宽度(Width),即占用内存大小是由 计算机字长编译程序 决定的。计算机字长提供了硬件计算精度的支持,编译程序则提供了源程序到机器码的转换。

下方为C/C++ 在通用系统或嵌入式系统中的常见数据类型的大小,所有值的单位均为 bit

BASIC DATA TYPE 8 bit MCU 16bit MCU (51) 32bit (x86) 32bit (TI F28004x)
CLA ON
32bit (TI F28004x)
CLA OFF
64bit (x86)
in C++ (g++)
64bit (x86)
in C (gcc)
char 8 8 8 8 8 8
short 16 16 16 8 16 16
int 16 32 32 16 32 32
long 32 32 32 16 64 64
long long / 64 64 32 64 64
float 32 32 32 32 32 32
double 32 64 16 64 64
long double / 32 128 128

下方内容转自网络,待验证。

(x86平台下)long intlong,给人的感觉好像是长整型,但实际上,它和 int 一样,只有32位。cppreference 给出的定义如下,但在实际的使用中,longint 几乎没有区别。

int - basic integer type. The keyword int may be omitted if any of the modifiers listed below are used. If no length modifiers are present, it’s guaranteed to have a width of at least 16 bits. However, on 32/64 bit systems it is almost exclusively guaranteed to have width of at least 32 bits. long - target type will have width of at least 32 bits.

实际上经过测试:在 x86_64 平台下,C 和 C++ 中的 long 长度都是要比 int 更长的,即 int 默认为 32bit 宽度,long64bit 宽度。

image-20211028101248341

Embedded System

嵌入式C语言中常用的数据类型如下图所示,而不同的

image-20211027155153218

double / float

IEEE二进制浮点数算术标准IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。

IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80比特实做)。只有32位模式有强制要求,其他都是选择性的。大部分编程语言都有提供IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE 754问世之前就有的C语言,现在有包括IEEE算术,但不算作强制要求(C语言的float通常是指IEEE单精确度,而double是指双精确度)。

该标准的全称为IEEE二进制浮点数算术标准(ANSI/IEEE Std 754-1985),又称IEC 60559:1989,微处理器系统的二进制浮点数算术(本来的编号是IEC 559:1989)。后来还有“与基数无关的浮点数”的“IEEE 854-1987标准”,有规定基数为2跟10的状况。现在最新标准是“IEEE 854-2008标准”。

Variable

C++ 与 ANSI C(C99标准)不同之处,在于后者只保证名称中的前63个字符有意义,即使第64个字符不同,但是只要前63个字符相同的变量则被认为是相同的。

在某些情况下,其他程序员会使用的变量命名风格及其意义:

  • strsz 前缀:表示以空字符结束的字符串
  • b 前缀:表示布尔值
  • p 前缀:表示指针
  • c 前缀:表示单个字符

Class

:(用户定义/标准的)数据类型规范,详细描述了如何表示信息以及对数据执行的操作。

对象:是根据类规范创建的实体(好似之于变量和基本数据类型)。

就像函数可以来自函数库一样,类也可以来自 类库。从技术上说,大部分类库都没有被内置到C++语言中,而是语言标准指定的类。类定义 位于类库文件中,并没有被内置到编译器里(有需要可以修改,虽然不建议)。

C++之所以如此有吸引力,很大程度上是由于存在大量支持UNIX、Macintosh 和 Windows 编程的类库。

类描述 指定了可以对类对象执行的所有操作。要对特定对象执行这些允许的操作有两种方法,一是直接使用类方法(本质即函数调用),二是重定义运算符(如 cincout 使用的 >><<

Function

被调用的函数称为 被调用函数(called function),包含函数调用的函数称为 调用函数(calling function)。

函数原型:即 函数声明,函数原型或声明只描述函数接口,指仅有函数头和分号 ; 而没有函数体的语句。函数原型定义了需要传递给函数的参数以及函数本身需要返回的值的类型。

库文件:存放了函数编译代码的文件。

头文件:包含了函数原型的文件。

image-20211028085847535

在有些语言中,有返回值的函数被称为 函数(function),无返回值的函数被称为 过程(procedure) 或 子程序(subroutine)。

但在C/C++中,都被称为 函数

Main Function

  • 有什么有些IDE中的main函数的括号里是带参数的?

是否在 主函数main() 参数括号 () 中使用关键字 void ,在C++和C中有明显的区别:

  • 在C++中,让括号空着和在括号中写 void 等效。
  • 而在C中,让括号空着意味着对是否接受参数保持沉默。

int main(int argc, const char * argv[]) 是UNIX和linux中的标准写法。int main() 只是默许的用法。

使用main函数时经常都是不带参数的,因此main 后的括号都是空括号。实际上,main函数可以带参数,这个参数可以认为是 main函数的 形式参数

C语言规定

  1. main函数的参数只能有两个,习惯上这两个参数写为 argcargv。因此,main函数的函数头可写为:main (argc,argv)
  2. argc (第一个形参)必须是 整型变量argv (第二个形参)必须是指向字符串的 指针数组

由于main函数不能被其它函数调用,因此不可能在程序内部取得实际值。那么,在何处把实参值赋予main函数的形参呢?

实际上,main函数的参数值是从 操作系统命令行/Terminal 上获得的。当我们要运行一个可执行文件时,输入空格,输入文件名,再输入实际参数即可把这些实参传送到main的形参中去。

argc 的数值会随着通过命令行传递给可执行文件的参数的增加而增加,即 argc 其实是代表着 参数的数量

image-20211027144512641

编译指令

using 是编译指令的关键字,

参考

  1. C++ Primer Plus
  2. int main(int argc, const char * argv[])
  3. C++ 类型系统
  4. IEEE二进制浮点数算术标准(IEEE 754)