因为工作需要,又要开始写c++了,但是c++发展的比较快,上学的时候虽然写过不少,但是也没有一个系统的学习。
在换工作期间,打算重新开始系统性的学习c++。这本书是我工作之前买的,一直没有读过,用的版本应该是c++11。现在好像已经出到很后面的版本了,一些新的特性等之后再学吧。
另外,文章主要为自己学习所用,如果内容能对看客提供些许帮助,不胜荣幸。
目录
本书共有18个大章节。外加一些附录内容。
会随着目录依次记录学习。但是一些非常基础的东西我应该会略过。
第1章 预备知识
本章节包括:
- c,c++发展历史与基本原理
- 过程性编程和面向对象编程
- c++如何在c基础上添加面向对象概念的
- c++如何在c基础上添加泛型概念的
- 编程语言标准
- 创建程序技巧
1.1 c++简介
略
1.2 C++简史
这一张主要介绍了c++的发展历史,我大块跳过了。
1.2.3面向对象编程
OOP(面向对象编程)强调的是数据,试图让语言来满足问题的要求。其理念是设计与问题的本质特性相对应的数据格式。OOP程序设计方式首先设计类,准确表示了程序要处理的东西。OOP还有助于代码的可重用。
C++中,类作为一种规范,描述这种新型数据格式,对象是符合这种规范设定所创建的数据。
1.2.4C++和泛型编程
c++支持泛型编程编程模式。与OOP的目标相同,都是为了重用代码和简化抽象概念。
不同点是,OOP其强调编程的数据方面,但是泛型编程强调的是独立于特定数据类型 。
1.4 程序创建的技巧
运行c++程序的具体步骤大体如下:
- 使用文本编辑器编辑代码,称之为源代码。
- 编译源代码,将源代码翻译成机器语言。
- 将目标代码和其他代码连接起来,连接至将目标代码同函数的目标代码与一些标准启动代码组合起来,生成程序的运行阶段版本,称之为可执行代码。
1.4.1创建源代码文件
C++实现 | 源代码文件的扩展名 |
---|---|
UNIX | C、cc、cxx、c |
GNU C++ | C、cc、cxx、cpp、c++ |
Digital Mars | cpp、cxx |
Borland C++ | cpp |
Watcom | cpp |
Microsoft Visual C++ | cpp、cxx、cc |
Freestyle Code Warrior | cp、cpp、cc、cxx、c++ |
1.5 总结
C语言新增了诸如控制结构和函数等特性。
C++增加了对面向对象编程和泛型编程的支持。
ISOC++标准(C++98/03和C++11)为确保众多实现的兼容提供了基础。
第2章 开始学习C++
本章节包括:
- 创建C++程序
- C++程序的一般格式
- #include编译指令
- main()函数
- cin输入
- cout输出
- C++注释
- 使用endl
- 声明和使用变量
- 定义和使用函数
2.1进入C++
C++语法的入口由函数头int main()开始。main()被启动代码调用,启动代码是编译器加到程序内的,是程序和操作系统的桥梁。
C语言中使用的写法是,int main(void),c++中淘汰了这种写法。这种写法等价于void main(),但是由于系统兼容性,应该尽量使用int main()的标准格式。
ANSI/ISO C++标准对于函数进行了更改,如果在最后没有加return 0的语句,那么会隐式加入这条语句,仅限于main()。
通常情况下,C++必须包含一个main()函数。
但是在一些特定的场景下,比如说编写一个动态链接库模块(DDL),由于DDL不是一个完整的程序,所以是可以没main()的;专用环境的程序,比如控制器芯片,可能不用main;一些编码框架,存在隐式的main。
2.1.2 C++注释
C风格的注释是使用/**/,C++的风格是使用//。
应该尽可能用C++风格的注释,避免出现问题。
2.1.3 C++预处理器和iostream文件
C++的预处理器会在程序进行主编译之前对文件进行处理,它处理名称以#为开头的编译指令。
例如,使用cin和cout需要包含#include<iostream>
2.1.4 头文件名
例如iostram这样的文件叫做包含文件,也叫头文件。
C语言的头文件中可能会使用扩展名.h,C++中可能会去掉.h而在前缀加上c,比如math.h改成cmath。而对于纯粹的c++库,则没有,比如iostream。
头文件类型 | 约定 | 示例 | 说明 |
---|---|---|---|
C++旧式风格 | 以.h结尾 | iostream.h | C++程序可以使用 |
C旧式风格 | 以.h结尾 | math.h | C、C++程序可以使用 |
C++新式风格 | 没有扩展名 | iostream | C++程序可以使用,使用namespace std |
转换后的C | 加上前缀c | cmath | C++程序可以使用,可以使用不是C的特性,如namespace std |
2.1.5 名称空间
名称空间是C++的一项特性,旨在编写大型程序时,能够将不同厂商的代码进行组合。
比如说两个已经封装好的产品中都包含wanda()名称的函数,那么在调用时,编译器讲不知道所指代的版本。使用名称空间的话,能够将产品封装在一个名称空间单元中,可以直接调用。那么上面的例子中,一个厂商可以将命名空间命名为Micro,那么函数的全程就变成了Micro::wanda()。
使用这种方法,类,函数,变量是C++编译器的基本组件,被放置在名称空间std中。
由于多数用户不喜欢引入名称空间,所以using编译指令应运而生,使用using namespace std;可以让人们不用在语句前加std::。
但是这实际上是一种偷懒的方式,在大型项目中容易有隐患。更好的方式是,只使所需要的名称可用:
using std::cout;
using std::endl;
using std::cin;
这样不必使用using namespace std便可以使用cin和cout。
当然,如果要使用其他定义,那么还需要加入using列表。
2.1.6 使用cout进行C++输出
从概念上看,输出是一个流,即从程序流出的一系列字符。
cout的对象属性包括一个插入运算符(<<),表示将右侧的数据插入到流中。
cout << "Hello World";
重载运算符
其实是将一个字符串语句插入到了输出流中。
可能注意到,插入运算符(<<)很像左移运算符(<<)。其实这是一个运算符重载的例子。通过重载,不同的运算符将有不同的含义。
C语言本身也有一些运算符重载的情况,比如&既表示取地址,又表示与位运算。*表示地址引用,又表示乘法。C++扩展了运算重载符的概念,允许用户为自定义类型定义新的运算方式。
控制符endl
endl也是在头文件iostream中定义的,位于命名空间std中。
endl表示重启一行。诸如endl等对cout有特殊含义的特殊符号被称为控制符。
换行符
C++中还可使用C语言的旧版方式,使用\n。
和endl的一个差别在于,endl确保程序继续运行前刷新输出(将其立即显示在屏幕上) ;使用“\n”不能提供这样的保证,意味着在有些系统中,有时可能在输入信息后才会出现提示。
2.1.7 C++源代码的格式化
C++中使用了分号来表示语句的结尾。说明可以将一条语句放置在多行上。
源代码中的标记和空白
一行代码中不可分割的元素叫做标记,空格、制表符、回车统称为空白。
C++源代码风格
- 每条语句占一行
- 每个函数有一个开始花括号和一个结束花括号,两个括号各占一行。
- 函数中的语句都相对于花括号进行缩进。
- 与函数相关的圆括号周围没有空白
2.2 C++语句
2.2.1 声明语句和变量
C++中使用声明语句来指出存储类型并提供位置标签。
比如int carr;提供了需要的内存和内存单元的名称。编译器来负责分配和标记内存的细节。
C++中,所有的变量都必须声明。在一些语言,比如basic中,可以在使用变量时创建变量,但是这样可能会出现拼写错误难以调试的问题,声明变量可以发现潜在的问题。
C++通常的做法是在使用变量之前进行声明。
2.2.2 赋值语句
赋值语句会将值赋值给存储单元。carr=25;其中=叫做赋值运算符。 C++中可以连续使用赋值运算符。
int a;
int b;
int c;
a=b=c=1;
赋值将从右向左进行。
2.2.3 cout的新花样
cout << carr;
首先,cout将carr替换成当前值25,然后把值转换成合适的输出字符。
cout的智能行为源自C++的面向对象特性。C++插入运算符将根据后面数据类型相应的调整其行为。是运算符重载的一个例子。
2.3 其他C++语句
2.3.1 使用cin
C++将输入看做流入程序的字符流。
iostream文件将cin定义为一个表示这种流的对象。
2.3.2 使用cout拼接
cout可以一条语句输出多个值。
2.3.3 类简介
类是C++中面向对象编程的核心概念之一。
cout是一个ostram类对象,cin是一个istream类对象,也是在iostream中定义的。
类描述了一种数据类型的全部属性(包括可使用它执行的操作),对象是根据这些描述创建的实体。
就想函数可以来自函数库一样,类也可以来自类库。C++之所以这么流行,很大程度上就是因为存在大量支持UNIX,Mac和windows编程的类库。
2.4 函数
2.4.1 使用有返回值的函数
比如C++中包含一个sqrt()的函数,可以返回平方根。
x = sqrt(6.25)
其中sqrt(6.25)被称为函数调用。sqrt称为被调用函数。6.25称之为参数。
在使用函数之前,C++编译器必须知道函数的参数类型和返回值类型。C++需要为每个使用到的函数提供原型。
比如,sqrt()的函数原型就像这样:double sqrt(double);
原型结尾的分号表明这是一条语句,是一个原型;如果省去分号,那么编译器将把这行代码解释为一个函数头。
不要混淆函数原型和函数定义。一般库文件中包含了函数的编译代码,而头文件中则包含了原型。
C++库函数存储在库文件中,编译器编译程序时,必须在库文件中搜索使用的函数。自动搜索哪些库文件,因编译器而异。
2.4.2 函数变体
函数也可以接受多个参数,或者不接收参数。
int rand(void); void明确指出,该函数不接受任何参数。如果省略void,括号为空,C++会将其解释为一个不接受任何参数的隐式声明。
不需要返回值的函数,也可以使用void指定返回类型。
2.4.3 用户定义的函数
一般的格式为,在int main() 之前定义函数原型,在之后实现函数定义。
int main() 的返回值不是返回给程序的其他部分,而是返回给操作系统。通常约定,返回值为0时一位置程序运行成功,否则意味着出现问题。
因此,如果C++程序无法打开文件,可以将其设计为返回一个非零值。
另外,C++存在专属关键字,比如return,int,void等,这些关键字是不能够作为变量名的。
2.4.4 用户定义的有返回值的函数
2.4.5 在多函数程序中使用using编译指令
通常有以下几种方式能够让程序访问名称空间std:
- 将using namespace std; 放在函数定义之前,让文件中所有的函数都能够使用名称空间std中的所有的元素。
- 将using namespace std; 放在特定的函数定义中,让该函数都能够使用名称空间std中的所有的元素。
- 在特定的函数中使用类似 using std::cout; 这样的编译指令,而不是using namespace std; ,让函数能够使用指定的元素。
- 完全不使用using,而是在需要使用名称空间std元素时,使用前缀std:: 。
2.5 总结
C++语句的类型:
- 声明语句
- 赋值语句
- 消息语句
- 函数调用
- 函数原型
- 返回语句
第3章 处理数据
本章内容包括:
- C++变量的命名规则
- C++内置的整形——unsigned long, long, unsigned int, int, unsignrf short, short ,char, unsigned char ,signed char, bool
- C++11新增的整形:unsigned long long, long long
- 表示各种整形的系统限制的climits文件
- 各种整形的数字字面值(常量)
- 使用const限定符号来创建符号常量
- C++内置的浮点类型:float, double, long double
- 表示各种浮点类型的系统限制的cfloat文件
- 各种浮点类型的数字字面值
- C++的算数运算符
- 自动类型转换
- 强制类型转换
3.1 简单变量
3.1.1 变量名
C++命名规则:
- 只能使用字母字符,数字,下划线。
- 第一个字符不能是数字
- 区分大写和小写字符
- 不能将C++关键字用作名称
- 以两个下划线(__fools)或者下划线和大写字母打头(_MyStars)的名称被保留给编译器使用。
- 以一个下划线开头的名称被保留给实现,用作全局标识符。
- C++对于名称的长度没有限制
如果想要使用两个或者更多的单词组成一个名称,通常情况下是使用下划线将单词分开。
在C++所有主观风格中,一致性和精度是最重要的。
3.1.2 整形
C++总共有10种整数类型可供选择。
描述存储的内存越多,整数值范围越大。
3.1.3 整形 short , int , long , long long
C++提供了一种灵活的标准,确保了最小长度,如下:
- short最少16位
- int至少和short一样长
- long至少32位,至少和int一样长
- long long至少64位,至少和long一样长
当前很多系统使用了最小长度,即short为16位,long为32位,但是不同的系统下int还是有多种选择。可能在一个系统中int为16位,另一个系统中为32位。
头文件climits中包含了关于整形限制的信息。定义了表示各种限制的符号名称,比如INT_MAX为int最大取值,CHAR_BIT为字节位数。
sizeof运算符支出,在使用8位字节系统中,int长度为4个字节,可对类型名和变量名使用sizeof运算符。
climits中的符号常量如下
符号常量 | 表示 |
---|---|
CHAR_BIT | char的位数 |
CHAR_MAX | char的最大值 |
CHAR_MIN | char的最小值 |
SCHAR_MAX | signed char的最大值 |
SCHAR_MIN | signed char的最小值 |
UCHAR_MAX | unsigned char的最大值 |
SHRT_MAX | short的最大值 |
SHRT_MIN | short的最小值 |
USHRT_MAX | unsigned short的最大值 |
INT_MAX | int的最大值 |
INT_MIN | int的最小值 |
UNIT_MAX | unsigned int的最大值 |
LONG_MAX | long的最大值 |
LONG_MIN | long的最小值 |
ULONG_MAX | unsigned long的最大值 |
LLONG_MAX | long long的最大值 |
LLONG_MIN | long long的最小值 |
ULLONG_MAX | unsigned long long的最大值 |
climits文件中包含与下面类似的语句行:
#define INT_MAX 32767
其中#define和#include一样,也是预处理器编译指令。意思为将所有的INT_MAX替换为32767。修改后的程序将在完成替换之后再进行编译。
#define是C语言遗留下来的,C++可以使用const来创建符号常量。所以不会经常用#define
然而被设计成用于C和C++的头文件中,必须使用#define。
对变量初始化的方式:
使用字面值或者其他常量来赋值:
int a=b;
int a=14;
如果不赋值,可能会产生未知错误。
C++11中使用{}对变量进行赋值。
int emus{12};
int emus = {12};
int emous = {}; // 大括号为空表示0值
使用这种方法可以防范类型转换错误。通过使用C++的大括号初始化,可作用于任何类型。是一种通用的初始化语法。
3.1.4 无符号类型
可以使用unsigned关键字进行声明,表示无符号。
3.1.5 选择整数类型
通常情况下使用int。
当有大型整数数组时,使用short有必要。
16位系统转到32微系统,int数组内存量会翻倍,但是short数组不受影响。
3.1.6 整形字面值
如果数字第一位为1-9,那么就是十进制。
如果数字第一位为0,第二位为1-7,那么就是八进制。
如果数字前两位为0x或0X,那么就是十六进制。
cout默认情况下是十进制输出的。(无论什么方式,在计算机中都是二进制数。)cout还提供了dec, hex , oct控制符,用于指示cout的输出格式。
using namespace std;
int a = 42;
// 10进制
cout << a<< endl;
// 16进制
cout << hex;
cout << a<< endl;
// 8进制
cout << oct;
cout << a << endl;
3.1.7 C++如何确定常量类型
C++编译器除非有理由,否则数字都存储为int。(除非存不下,或者有特殊后缀指定)。
后缀为L表示为long常量。
后缀U表示为unsigned int常量。
后缀为LL表示为long long常量。
3.1.8 char类型:字符和小整数
在美国最常用ASCII字符集。
C++支持的宽字符可以存储更多值。比如Unicode。
可以用cout.put(ch); 显示一个字符。意思为通过类对象cout使用函数put()。
对于特殊的字符,C++提供了转义序列。
Unicode提供了一种表示各种字符集的解决方案——为大量字符和符号提供标准数值编码,ASCII是Unicode的子集。
与int不同,char既不是没有符号也不是有符号,取决于编译器。如果想做微数值类型使用,那么unsigned char和signed char的范围是有差距的。0-255 和-128-127。
C++11中新增了char16_t和char32_t,均为无符号。
C++使用前缀u表示char16_t字符常量和字符串常量,比如u"C"和u"be good"。
C++使用前缀U表示char32_t字符常量和字符串常量,比如U"C"和U"be good"。
类型char16_t和/u00F6形式的通用字符名匹配。
类型char32_t和/U0000222B形式的通用字符名匹配。
3.1.9 bool 类型
非零值转为true;零值转为false.
3.2 const限定符
使用const修饰的变量被初始化后,编译器不允许再修改值。
一种常见的方法是将名称首字母大写,表示为常量。另一种约定是将整个名称大写。
const之所以比#define好,首先,能够明确指定类型,其次,可以使用C++的作用域规则限制在特定函数或者文件;第三,可以用于更加复杂的类型。
3.3 浮点数
浮点数可以表示小数部分。
计算机会对浮点数进行两部分存储,一部分表示值,另一部分表示对值放大或者缩小。
比如,34.1245和34124.5。第一个数表示为0.341245(基准值)和100(缩放因子);第二个数表示为 0.341245(基准值一样)和10000(缩放因子更大)。
缩放因子是2的幂,不是10的幂。
3.3.1 书写浮点数
一种方法是使用常见的书写方式,比如1.56 , 0.13 , 8.0。
第二种是使用E或者e,比如2.5e+8 , 7e5 , 9.11e -32。
称为浮点数,就是因为小数点是可以移动的。
3.3.2 浮点类型
三种:float ,double , long double。
是按照可以表示的有小数位和允许的指数最小范围来描述的。
有效位是数字中有意义的位。
C++对于有效位数的要求为:float至少32位,double至少48位,long double至少和double一样。但是三种类型的有效位数可以一样多。
通常情况下,float为32位,double为64位,long double为 80/96/128位。
这三种类型的指数范围至少为-37到37.
在cfloat中可以找到系统的限制。
使用cout.setf(ios_base::fixed, iosbase::floatfield); 可以迫使输出使用定点表示方法,防止使用E表示法,并将程序显示到小数点后6位。ios_base::fixed, iosbase::floatfield为iostream中的常量。
3.3.3 浮点数常量
通常情况下,浮点数会被保存为double,如果想保存为float,需要在后面加f/F后缀。
对于long double 可以使用L/l后缀。
1.234f
2.45E20F
20.2L
3.3.4 浮点数的优缺点
浮点数的优点在于表示的范围更宽。
缺点在于精度低,而且运算速度没有整数快。
3.4 算数运算符
C++提供了+ - * / %五种基本运算符。
3.4.4 类型转换
C++自动执行很多类型转换:
- 将算数类型的值赋给另一种算数类型的变量时
- 表达式中包含不同的类型时
- 将参数传递给函数时
在进行类型转换时,通常会将一个值赋给取值范围更大的类型。
但是将大值赋值给一个更小的类型时,可能会出现精度不够,超出范围等问题。
C++11的校验表:
- 一个操作树为long double,将另一个操作数转为long double
- 否则,一个操作数为double,将另一个操作数转为double
- 否则,一个操作数为float,将另一个操作数转为float
- 否则,说明操作数都是整形,执行整形提升
- 若都为有符号或者都为无符号,转换为高级别整数
- 若一个有符号一个无符号,且无符号操作数级别比较高,那么需要转换为无符号操作数类型
- 否则,如果有符号类型可表示为无符号类型的所有可能取值,将无符号操作数转换为有符号操作数类型
- 否则,将两个操作数都转为有符号类型的无符号版本
简单说就是:
long long > long > int > short > signed char > bool
C++可以使用int(a)或者static_cast<int>(a)来进行类型的强制转换。
3.4.5 C++11的auto声明
auto可以让编译器根据初始值类型推断变量类型。
在处理复杂类型,比如stl库中的类型时,会很好用。
3.5 总结
C++的数值类型,整数,浮点数,操作符,类型转换。