第一章:开始
1.1编写一个简单的C++程序
函数定义包括哪四个部分?
return类型和函数返回类型“相容”是什么意思?#
数据、变量、类型的关系是什么?
iostream库中的stream如何理解?
iostream库中的四个对象,注意不是类
对象名 | 功能 |
---|---|
cin | 标准输入 |
cout | 标准输出 |
cerr | 标准错误 |
clog | 一般信息 |
#include <iostream>
这里的iostream是头文件还是源文件?
"<<"是一个什么运算符?左右两侧的“类型”要求是什么?运算返回结果是什么?
">>"是什么运算符?左右“类型”要求是什么?
如何理解“字符串字面值常量”?#
endl作为一个操纵符,它操纵了什么?
如何理解刷新缓冲区?
std是一个命名空间,作用是什么?
作用域运算符“::”的作用是什么?
单行注释结束符是什么?
多行注释可嵌套吗?为什么?
多行注释应该采取什么风格,让多行注释更加显眼?
在什么情况下可能会发生对多行注释的嵌套?如何进行?
控制流的部分跳过。
类的简单介绍跳过。
第二章:变量和基本类型
2.1 基本内置类型
基本数据类型包括哪些?算术类型+空类型(void)
算术类型包括哪些?字符、整型数、布尔值、浮点数。
空类型有哪些使用场景?
2.1.1 算术类型
算术类型的大小是固定的吗?
四种char的区别?
整型的区别?
阅读34页“内置类型的机器实现”,回答问题:
各种内置类型在机器层面的形式是什么?0和1
既然不同内置类型在机器层面的形式相同,那么如何进行区分?
浮点型:(一般情况下)
fload : 一个字(转换为十进制约有7个有效位)
double : 两个字(转换为十进制约有16个有效位)
long double : 三或四个字
signed和unsigned可以加在int \ short \ long \ long long 之前,表示有符号、无符号,如果不加,默认有符号
signed和unsigned可以加载char之前,表示有符号、无符号,如果不加,并不是默认有符号
数据类型选择的建议!
1、在满足条件的情况下,尽量选择无符号类型。
2、在int范围不够的情况下,尽量选择long long。
3、不要用char进行数值计算。
4、对于较小的整数,最好明确指定有无符号。
5、浮点运算尽量使用double。
说明:
1、无符号类型范围较大。
2、long类型和int类型常常有一样的尺寸。
3、char类型有时是有符号的,有时又是无符号的。
4、float通常精度不够,在运算上也不比double有很大的优势。
2.1.2 类型转换
如果无符号数和有符号数混合计算会发生什么?
无符号数和无符号数计算应该注意什么?
此外,无符号数++,无符号数–也应当注意在>=0的范围内进行计算。
整型字面值有无类型?是什么类型?
字符串对应什么数据结构?为什么实际长度比内容多1?
字符串分行书写的方式:
"first line "
“and second line”
常见的转义字符有:
换行\n 横向制表\t 报警\a 退格\b 双引号" 单引号’ 反斜线\ 问号? 回车\r
字面值的内容跳过
2.2 变量
2.2.1 变量定义
定义时可以不进行初始化,但是初始化必须在定义时进行。
初始化和赋值的区别是什么?
什么是默认初始化?
默认初始化在什么情况下不会发生?会造成什么影响?
介绍一种特殊的初始化形式:列表初始化,它使用花括号来进行初始化。
2.2.2 变量声明和定义的关系
分离式编译,如何理解?
分离式编译,如何实现?为什么?
声明和定义的区别?
变量只能被定义一次,但是可以被多次声明
静态类型,在编译阶段检查类型。编译器在对象处在错误的运算语句中时能够及时发现,前提是让编译器事先知道该对象的类型。这也是先声明后使用的原因。
2.2.3 标识符
大小写、不与语言自带标识符冲突、不能连续两个下划线
下划线不能紧连大写字母
命名规范:
- 见名知义
- 变量名消息字母
- 类名大写字母开头
- 多个单词之间应当区分,比如加下划线或者采用驼峰命名
2.2.4 作用域
作用域:变量有效的区域,大多以花括号分割。
作用域可以嵌套,内外层出现同名变量时(我们应当避免这种情况发生),优先访问内层变量,内层如果想要访问全局变量,可以使用作用域操作符双冒号::来访问,::左侧为空说明想要访问全局变量。
2.3 复合类型
2.3.1 引用
引用相当于给对象起一个别名。
引用必须初始化,绑定对象(引用初始化)是一个引用的使命。诞生之初就要明确,且绑定的对象不得更改。
注意:
- 不能定义引用的引用(引用本身不是对象,只是一个名字)
- 引用不能绑定字面值(只能绑定对象)
2.3.2 指针
指针和引用对比
引用 | 指针 |
---|---|
不是对象,只是一个名字 | 是一个对象 |
必须初始化 | 可以不初始化 |
不可更改引用的对象 | 可以更改指向的对象 |
“&”
语句:
int age = 20;
int &refAge = age;
int *pAge = &age ;
int ageValue = *pAge ;
指针4种状态
- 指向一个对象
- 可以通过指针来间接访问对象
- 指向对象的下一个位置
- 空指针
- 这两种情况虽然没有指向任何对象,但是是有效指针。
- 无效指针
- 可能会在编译器不觉察的情况下,造成严重后果,因为它在错误的使用下会修改我们本不希望修改的内存数据。
- 往往是程序崩溃的原因。又往往难以定位。
- 一种情况是:一个指针没有被初始化,那它就是无效指针。注意:空指针也需要初始化。虽然没有被初始化,但是很有可能程序在运行时发现这个指针指向一个对象!这会造成严重的后果。
- 所以任何一个指针虽然不明确要求初始化,但是在编程时一定要及时赋值,实在不行就初始化为nullptr,避免无效指针引起程序崩溃。
指针的特殊使用
用在条件句中:if(pa) 如果pa是空指针,则返回false,如果指向一个对象则返回true
用在比较句中:if(pa==pb),返回true的条件
- pa/pb存放的地址相同
- 指向同一个对象
- 指向同一个对象的下一个地址
- 都为空
2.3.3 较复杂的情况
int a,*pA,&refA ;
//一句话声明了三个不同类型的变量:int、指针、引用
int a = 1;
int *pA = &a;
int *&p = pA;
//理解即可,最最好别这么写
2.4 const限定符
const对象必须初始化。
int i = 1;
const int j = i;
//这样做是可以的,含义是将当前i的值拷贝给const变量j
- 编译器在编译的时候,将const类型的变量替换为对应的值。
- 于是我们发现以下两个需求是冲突的:
- 由于变量不允许重复定义,在一个文件中定义一个const类型的变量后,在其他文件中只能声明这个const类型的变量
- 每一个文件必须能够访问到const变量对应的值,否则无法编译
- 为了解决这个冲突,c++中默认设置为:const变量仅在本文件中有效。这样即使在多个文件中定义同一个变量也不会冲突。
- 如果确实需要共享
2.4.1 const的引用
对const的引用,在定义时也需要声明为const:
const int num = 1;
const int &refNum = num; //对
int &refNum = num; //错
//引用const类型,则必须以这样的格式定义:const int &变量名 = 引用对象。这并不是说这个引用本身是个常量,而是说它指向const类型就应该这么写。这里const这个单词是修饰它指向的数据类型的,不是修饰它本身的。
int &refNum = 10; //错,引用的初始值必须是一个对象
const int num2 = 2;
refNum = num2; //错,引用不能更改
int num3 = 3;
const int &refNum3 = num3; //对,可以让refNum3自以为引用了一个常量
int &ref2Num3 = num3;
refNum3 = 10; //错,虽然num3本身不是const类型的,但是refNum3以为它是const类型的,所以不能通过refNum3来修改Num3
ref2Num3 = 10; //对,它不是const引用。
double dval = 1.1;
const int &ri = dval;
等价于:
double dval = 1.1;
int temp = dval; //temp = 1
const int &ri = temp;
//这是合法的,其中temp成为临时量。这里的temp只是帮助我们理解的,实际上它没有名字。但是不建议这样写,因为没有意义。
而对于不是const的情况:
int &refDval = dval;
这是非法的,由于临时量的存在,我们无法通过refDval来修改dval,这就使得这一引用毫无意义。
//如果绑定一个字面量:
const int &refInt = 10;
则也是创建了一个临时量temp=10供refInt来绑定。那么以下写法也是错误的,因为毫无意义
int &refInt2 = 10;
2.4.2 const和指针
一个指针可以认为它指向了一个const类型的变量,不管它是不是真的
最易混淆的是两种指针:指向常量的指针和常量指针
指向常量的指针
const double dval = 3.14;
const double *pDval = &dval;
//将变量的地址赋予指针
//第二行中的const并不是修饰pDval本身的,而是说pDval的指向:指向一个常量
//任何一个指向常量的指针,都应该使用第二行这样的格式
//可以更换指向的对象,只要待更换的对象也是常量
const double dval2 = 11.1;
pDval = &dval2;
//不可以改变对象的值
*pDval = 22.2; //这是错误的
常量指针
int num = 1;
int *const pNum = #
//不可以更换指向的对象
//可以通过*pNum = 2;这样的方式来改变对象的值。
2.4.3 顶层底层const、指针之间的赋值
指向常量的常量指针:
const int *const pToConstNum = &num2;
这里第一个const是底层const,修饰所指对象,第二个是顶层const,修饰指针本身
(const int *) = (int *) :正确
(int *) = (const int *):错误
上面的两个const都是底层const,这体现了底层const对指针之间赋值操作的限制。
2.4.4 constexpr、常量表达式、字面值类型
什么是常量表达式?
常量表达式最简单的举例:字面量
const int a = 1; //是
const int b = a+1; //是
int c = 1; //不是
const int d = getNum(); //不是
getNum()会随着运行而改变,在编译期间不能得到计算结果。
constexpr
constexpr int e = getNum();
//只有当getNum()不随着运行而改变、在编译期就能得到计算结果时,这条语句才是正确的。
//当满足这个条件时,尽量将语句添加constexpr声明而不是用const,这样可以减少程序错误,增加可读性,便于优化。
字面值类型
值很简单的类型。
比如:int / long / short / double / bool 这些算术类型
比如:int &refNum 这种引用类型
比如:int *pNum 这种指针类型
值不简单的类型不是字面值类型:
比如:类(对象)、string类型
constexpr指针它的初始值必须是以下三种情况:
- nullptr
- 0
- 固定地址中的对象
constexpr指针声明方式:虽然声明在底层const的位置,但是作用相当于一个顶层const,不许修改所指的对象:
constexpr int *q = nullptr;
2.5 处理类型
2.5.1 类型别名
typedef int size; //基本类型
typedef size *pSize; //复合类型
using count = size; //新的写法
难点在于处理复合类型比如指针的情况:
比如:复合再复合:
typedef int *pInt;
pInt *ppInt;
//这里的ppInt是一个指向指针的指针(二维)
再比如:复合加const
typedef char *pString;
const pString cpStr;
//这里的cpStr是一个常量指针,而不是指向常量的指针
2.5.2 auto类型说明符
auto的作用:
让编译器通过初始值来推算变量的类型。
int a = 1, b = 2, &d = a;
auto c = a + b;
//则推算c的类型为int
auto c = a + b, c2 = 1.1;
//错误,因为c是整型而c2是浮点型,不一致。
//这是一条声明语句,一条声明语句只能有一个基本数据类型
//auto特点:忽略顶层const,推算底层const
auto left( int ) = right( int );
auto left( int ) = right( const int ); //忽略顶层const
const auto left( int ) = right( int ); //显式说明顶层const
auto left( int 星 ) = &right( int ); //推断指针的方式
auto left( const int 星 ) = &right( const int ); //指针的底层const
const auto left( int 星 ) = right( int 星 ); //指针的顶层const
const auto left( int 星 ) = &right( int ); //指针的顶层const
auto left( int ) = right( int & ); //引用符号&被忽略,想使用引用只能用下面的方法
auto &refRight ( int & ) = right( int ); //绑定
const auto &refRight ( const int & ) = right( int / const int / 字面值 ); //引用的底层const
decltype 类型指示符
decltype() 不会对顶层const进行特殊处理,也不会消除引用
decltype() 可以传入表达式,包括函数:func(),注意写上括号
特殊的表达式包括:
decltype(整型引用) 整型引用
decltype(整型引用 + 整型) 整型
decltype(整型解引用) 整型引用
decltype((整型)) 整型引用
decltype(值):不加括号看作值
decltype((表达式)):加一层或多层括号看作表达式