C++ Primer 笔记 第二章
C++会用是会用了,还是感觉基础不牢,于是励志从今天起开始每天看一点C++ Primer,争取到学期末时候把它精读完
这篇笔记仅记录了我感到不够熟悉或不够明确的部分,仅作个人学习记录
2021.3.29
第二章
2.1 基本内置类型
2.1.1 算术类型
一些不常用类型
| 类型 | 含义 | 最小尺寸 |
|---|---|---|
| wchar_t | 宽字符 | 16位 |
| char16_t | Unicode字符 | 16位 |
| char32_t | Unicode字符 | 32位 |
- Unicode字符是用于表示所有自然语言中字符的标准
2.1.2 类型转换
类型转换
- 给无符号类型赋超出其表达范围的值时,结果取余(即按位截断)
- 给有符号类型赋超出其表达范围的值时,结果是未定义的(undefined),此时程序可能继续工作,可能崩溃,可能产生垃圾数据
编程建议
-
避免有符号无符号类型混用
-
应避免依赖于实现环境的行为(不可移植) eg. 把int的大小看作一个确定不变的已知值
2.1.3 字面值常量
整型&浮点型
-
十进制默认有符号,8/16进制可能有可能没有
-
8进制: 0开头的整数 eg. 024
-
16进制: 0x或0X开头 eg. 0x14
-
浮点型: 默认为double,可以用小数/科学计数法表示,指数部分用E/e标识
字符&字符串
-
字符——单引号
-
字符串——双引号
字符串在结尾处会自动添加一个空字符 ‘\0’
转义序列
不熟悉的符号:
| 符号 | 含义 |
|---|---|
| \a | 报警(响铃)符 |
| \b | 退格符 |
| \r | 回车符(return) |
| \f | 进纸符 |
指定字面值的类型
- 字符&字符串:
| 前缀 | 含义 | 类型 |
|---|---|---|
| u | Unicode 16 | char16_t |
| U | Unicode 32 | char32_t |
| L | 宽字符 | wchar_t |
| u8 | UTF-8(仅用于字符串) | char |
- 整型:
| 后缀 | 最小匹配类型 |
|---|---|
| u / U | unsigned |
| l / L | long |
| ll / LL | long long |
- 浮点型:
| 后缀 | 类型 |
|---|---|
| f / F | float |
| l / L | long double |
2.2 变量
2.2.1 变量定义
初始化不是赋值,初始化是创建变量时赋予其一个初始值,赋值是把对线的当前值擦除,并以一个新值来代替
-
列表初始化(list initialization)
//均可初始化一个名为unit_code的int变量 int units_code{0}; //列表初始化 //int u{2.333} //编译报错,初始值存在丢失信息的风险 int unit_code(0); int u(2.333) //编译通过,丢失部分值 -
默认初始化(default initialized)
- 定义于任何函数体外的变量被初始化为0;
- 定义证函数体内部的内置类型变量不被初始化,值为undefined,拷贝或访问它将引发错误
- 每个类各自决定其初始化对象的方式,决定是否允许不经初始化就定义对象(若允许,则由类决定对象的初始值是什么)
建议初始化每一个内置类型的变量。
2.2.2 变量声明和定义的关系
C++支持分离式编程(separate compilation)机制,将声明(declaration)和定义(definition)区分开来,变量只能被定义一次,但是可以多次声明。若一个文件想使用别处定义的名字则必须包含对其的声明,即在变量名前添加关键字extern,例如extern int i;
extern若包含初始值则变成定义,例如extern double i=2.333;是定义而非声明
若在函数体内部,试图初始化一个extern标记的变量,会引发错误
- C++是静态类型(statically typed)语言,在编译阶段检查类型,即进行type checking,故使用变量前必须声明其类型
2021.4.11
2.2.3 标识符
- 命名规范:
- 能体现实际含义
- 变量名小写字母开头,命名格式为驼峰式(someName)或下划线形式(some_name)
- 自定义类名以大写字母开头
2.2.4 名字的作用域
显式访问全局变量:::variableName
2.3 复合类型
2.3.1 引用
引用主要指左值引用(lvalue reference)
引用为对象其了另外一个名字(即别名),引用类型必须被初始化,无法令引用重新绑定到另一个对象。
定义引用时程序把引用和它的初始值绑定在一起,而非拷贝初始值。
- 引用并非对象,故不可定义引用的引用
2021.4.13
2.3.2 指针
- 指针本身是对象,允许赋值和拷贝,无需赋初值
- 若指针未被初始化则也将拥有一个不确定的值(此时为无效指针),拷贝/访问无效指针的值将引发编译器检查不到的错误
C++11标准引入nullptr来初始化指针
若想使用NULL则需#include <cstdlib>,相当于用0初始化指针。
- 不可将int型的0直接赋值给指针
- 现在的C++程序应最好使用
nullptr,同时尽量避免使用NULL. - 建议初始化所有指针
void* 指针:
- 可存放任意对象的地址,对该地址中是什么类型的对象并不了解
- 无法访问且不可直接操作
void*指针所指向的对象
2.3.4 理解复合类型的声明
-
定义多个变量
int* p1,p2; //p1为int指针,p2为int -
指向指针的指针
int i=1; int *pi=&i; int **ppi=π //ppi为一个指向一个int型指针的指针 -
指向指针的引用
int i=2; int *p=nullptr; int *&r=p; //r为对指针p的引用 r=&i; //r指向i,即p指向i *r=3; //修改r指向内容的值,即将i值改为3 -
不可定义指向引用的指针(引用非对象)
-
理解变量类型时,最简单的方法为从右向左阅读其定义,离变量名最近的符号对其类型有最直接的影响。
2021.4.22
2.4 const限定符
-
const对象必须初始化
-
默认状态下,const对象仅在文件内有效
若想在不同文件中共享const对象,则应在定义与声明时都添加
extern关键字
2.4.1 const的引用
-
可以引用const常量,但不可用作修改它的值
-
不可将常量引用赋值给非常量引用
int i=42; const int &r1=i; //正确,r1为常量引用,不可通过r1修改i的值,但仍可以直接修改i的值 const int &r2=r1*2; //正确,r2也为常量引用 int &r3=r1+1; //错误,不可将常量引用赋值给非常量引用
2.4.2 指针和const
-
想存放常量对象的地址,只能使用指向常量的指针(pointer to const),且不可用于改变其所指对象的值
-
允许指向常量的指针指向一个非常量
double x=1.23; double &y=x; const double *ptr=&x; //正确 //ptr=2; //错误,不可通过ptr修改x的值 x=3.21; //正确,可通过其他途径改变x的值 y=4.32; //正确 -
const指针:
-
常量指针(const pointer)必须初始化
int x=1; int *const ptrx=&x; //ptrx中存放的地址不变,可用于修改其中对象的值 *ptrx=2; //正确,x的值被修改为2 const double y=2.19; const double *const ptry=&y; //ptry中存放的地址不变,地址中存放的变量不变, //即ptry为指向常量对象的常量指针 -
从右向左阅读,例如
const double *const ptry中,离变量名最近的为const表示其为常量对象,在这之后,*表示其为一个常量指针,double表示它是指向一个double类型的常量指针,最后,const表示它是一个指向一个double常量对象的常量指针
-
2021.4.23
2.4.3 顶层const
顶层const(top-level cosnt)表示指针本身是个常量
底层const(low-level const)表示指y所指的对象是一个常量
- 执行拷贝操作时,顶层const不受影响,而拷入拷出的对象必须具有相同的底层const资格,或是两个对象的数据类型能够转换,一般来说,非常量可以转换为常量,反之则不行
定义中类型左侧为底层const,右侧为顶层const
2.4.4 constexpr和常量表达式
常量表达式(const expression)指值不会改变且在编译过程中就能得到计算结果的表达式,有数据类型和初始值共同决定
- C++11新标准中允许将变量声明为
constexpr类型以便由编译器来验证变量的值是否是一个常量表达式
constexpr int m=20;
constexpr int l=m+1; //正确
constexpr int sz=size(); //仅当size是一个constexpr函数时才是正确的声明语句
-
声明
constexpr时的类型只能为“字面值类型”(literal type),目前为止接触过的字面值类型包括算术类型,引用,指针 -
一个
constexpr指针的初始值只能为nullptr或0,或是存储于某固定地址中的对象- 一般来说,函数体内定义的变量并非在固定地址中,
constexpr指针不可指向主义的变量 - 定义在所有函数体外的对象地址固定,可以用来初始化
constexpr指针
- 一般来说,函数体内定义的变量并非在固定地址中,
-
constexpr把它所定义的对象置为了顶层constconst int *p=nullptr; //p是指向整形常量的指针 constexpr int *q=nullptr; //q是指向一个整数的常量指针(顶层const) constexpr int i=1; constexpr const int *r=&i; //r是常量指针,指向整型常量i
2.5 处理类型
2.5.1 类型别名
传统方法:
typedef double wages,*p; //wages是double的别名,p是double*的别名
新方法——别名声明(alias declaration)
using W = double;
- 指针、常量和类型别名
typedef char *pstring; //pstring=char*
const pstring *ps;
const char* *ps; //与上一句等价
2021.4.25
2.5.2 auto类型说明符
-
auto定义的变量必须有初始值 -
在一条语句中使用
auto声明多个变量时,该语句中所有变量的初始基本数据类型必须一样 -
在使用引用作为初始值时,以引用的对象的类型作为auto的类型
-
auto一般忽略顶层const,保留底层const
int i=0, &ir=i;
const int ci=1, &cr=ci;
auto a = &ci; //a是指向常量的指针(底层const)
/*错误:*/const auto &b = 2; //不可以将非常量引用绑定字面值
const auto *b = &ci; //b是指向常量的常指针(顶+底)
2.5.3 decltype类型指示符
decltype选择并返回操作数的数据类型
decltype(func()) sum = x; //并不实际调用函数func,只使用其返回类型
decltype返回包括顶层const与引用在内的所有属性
int m=1;
const int i=0, &ir=i;
double pi=3.14, *ptr=π
decltype(ir) x=9; //x的类型为const int&
decltype(pi+i) y; //y的类型为double
decltype(ir+3) z; //虽然ir类型为const int&,但加法表达式的结果为int,故z的类型为int
decltype(*ptr) w=4; //w为double&,必须初始化
decltype((m)) a=9; //a为int&,因为(m)被视为一种可以作为赋值语句左值的特殊表达式,得到引用类型
- decltype((variable))的结果用于是引用
2.6 自定义数据结构
类内没有初始值的成员将被默认初始化(见2.2.1节)
类内初始值或放在花括号里,或放在等号右边,不可使用圆括号
- 头文件通常只包含只能被定义一次的实体,如类、
const和constexpr变量
预处理器(preprocessor)
-
头文件保护符(header guard)
#define把一个名字设定为预处理变量#ifdef当且仅当变量已定义时为真#ifndef当且仅当变量未定义时为真一旦检查结果为真,则执行后续操作直至
#endif指令为止,否则忽略中间部分所有语句 -
头文件保护符必须唯一,一般做法是基于头文件名构建,并全部大写
#ifndef MAIN_H
#define MAIN_H
const int i=0;
#define PI 3.14
#endif
本文记录了C++ Primer第二章的学习笔记,涉及算术类型、Unicode字符、类型转换、字面值常量、变量、复合类型(引用与指针)、const限定符、auto与decltype,以及自定义数据结构的处理。

541

被折叠的 条评论
为什么被折叠?



