补充C++基础笔记。
变量和基本类型
变量
1. 带符号类型和无符号类型不能混用。
如a=1, b=-1, 且a和b都是int,则表达式a * b
的值为-1;如过此时a是int,b是unsigned类型,则结果须视当前机器上int所占位数而定(a转成unsigned后运算)。
2. 初始化和赋值的区别:初始化的含义是创建变量时赋予其一个初始值,而赋值的含义时把对象的当前值擦除,而以一个新值来替代。
3. 对象:具有某种数据类型的内存空间。
定义于函数体内的内置类型的对象如果没有初始化,则其值未定义。类的对象如果没有显示地初始化,则其值则由类确定。
4. 变量的声明和定义:
声明:使得名字为程序所知 -> 规定了变量的类型和名字
extern int i;
//声明i
定义:负责创建与名字关联的实体 -> 类型、名字、申请存储空间、可能会为变量赋一个初值
int j;
//声明并定义了j
任何包含了显式初始化的声明即成为定义。
变量能且只能被定义一次,但是可以被多次声明 -> 多个文件使用同一个变量时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行说明,却绝对不能重复定义。
5. 变量命名规范:
变量名一般用小写字母;自定义的类名以大写字母开头;多个单词中间有下划线_
6. 名字的作用域:
作用域(scope)是程序的一部分,在其中名字有其特定的含义,C++中大多数作用域都以花括号分隔。
复合类型:引用和指针 -> 更加复杂的声明符
1. 引用
引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。
int ival = 1024;
int &refVal = ival; //refVal指向ival(是ival的另一个名字)
int &refVal2; //报错:引用必须初始化
2. 指针
指针本身就是一个对象 -> 允许赋值和拷贝,生命周期内可以先后指向不同的对象;
1)指针无需再定义时赋初值。
double dp, *dp2; //dp2是指向double型对象的指针,dp是double型对象
int ival = 42;
int *p = &val; //这里的&为取地址符,p存放变量ival的地址,或者说p是指向变量ival的指针
double dval;
double *pd = &dval; //正确:初始值是double型对象的地址
double *pd2 = pd; //正确:初始值是指向double对象的指针
int *pi = pd; //错误:指针pi的类型和pd的类型不匹配
pi = &dval; //错误:试图把double型对象的地址赋给int型指针
2)指针的类型 <-> 指向对象的类型 ~ 要匹配
3)指针的四种状态:指向一个对象、指向紧邻对象所占空间的下一个位置、空指针(没有指向任何对象)、无效指针(其他情况)
指针指向一个对象 -> 解指针符*来访问该对象:
int ival = 42;
int *p = &ival; //p指向变量ival
cout << *p; //*p代表ival
4)空指针:不指向任何对象
生成空指针的办法 -> 空指针可以转换为任意其他的类型指针
int *p1 = nullptr; //等价于int *p = 0;
int *p2 = 0; //初始化为字面常量0
int *p3 = NULL; //等价于int *p3 = 0; 这一操作需要首先#include cstdlib
//不可以直接将int变量赋值给指针
//建议:初始化所有指针
5)void指针:可以存放任何指针,但是不能直接操作void*指针所指的对象[不知道对象的类型,不知道能做什么操作]
6)指向指针的指针:
int ival = 1024;
int *pi = &ival; //pi指向一个int型的数
int **ppi = π //ppi指向一个int型的指针
7)指向指针的引用:引用本身不是对象,因此不能定义指向引用的指针,但指针是对象,所以存在对指针的引用。
int i = 42;
int *p;
int *&r = p; //r是一个对指针p的引用
const 限定符
- 目的:使得变量的值不能被改变
const int i = get_size(); //正确:运行时初始化
const int j = 42; //正确:编译时初始化->在编译过程中会把用到该变量的地方都替换成对应的值
const int k; //错误:k是一个未经初始化的常量
- 如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
例子:extern const int bufSize = fcn();
- const的引用:不能修改其所引用的对象
int i = 42;
int &r1 = i; //引用ri绑定对象i
const int &r2 = i; //r2也绑定对象i,但是不允许通过r2修改i的值
//注:此时r2会随着i的变化而变化
r1 = 0; //i的值修改为0
r2 = 0; //错误:r2是一个常量引用
- 指针和const
1)指向常量的指针(pointer to const)
指向常量的指针不能用于改变其所指对象的值;
const double pi = 3.14;
const double *cptr = π
*cptr = 42; //错误:不能给*cptr赋值
允许令一个指向常量的指针指向一个非常量对象(此时依然不能通过此指针改变所指对象的值)-> 特例
double dval = 3.14;
cptr = &dval; //正确:但是不能通过cptr改变dval的值
2)const指针(const pointer):将指针本身定义为常量 -> 存放在指针中的地址不能改变 -> 但指针指向的值可以改变
int errNumb = 0;
int *const curErr = &errNumb; //curErr将一直指向errNumb
const double pi = 3.14159;
const double *const pip = π //pip是一个指向常量对象的常量指针
*curErr = 3; //正确:const指针指向的值可以改变
*pip = 2.7; //错误:指向常量的指针 指向的值不能通过指针改变
3)区分:底层(top-level)const和顶层(low-level)const
顶层const:对象本身是个常量
底层const:指针所指的对象是个常量
int i = 0;
int *const p1 = &i; //不能改变p1的值,这是一个顶层const
const int ci = 42; //不能改变ci的值,这是一个顶层const
const int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const
拷贝操作:顶层const不受影响,底层const的拷入拷出对象必须具有相同的底层const资格,或者两个对象的数据类型能够转换
i = ci; //正确:ci是一个顶层const,对此操作无影响
p2 = p3; //正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响
int *p = p3; //错误:p3包含底层const的定义,而p没有
p2 = &i; //正确:int*能转换成const int*
int &r = ci; //错误:普通的int&不能绑定到int常量上
const int &r2 = i; //正确:const int&可以绑定到一个普通int上
4)constexpr和常量表达式 -> C++11新特性
声明为constexpr的变量一定是一个常量
constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + 1; //mf+1是常量表达式
constexpr int sz = size(); //只有当size是一个constexpr函数时才是一条正确的声明语句
指针和constexpr [constexpr将其所定义的对象置为了顶层const]
const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr; //q是一个指向整数的常量指针
constexpr int *np = nullptr; //np是一个指向整数的常量指针,其值为空
int j = 0;
constexpr int i = 42; //i的类型是整型常量
//i和j都必须定义在函数体外
constexpr const int *p = &i; //p是常量指针,指向整数常量i
constexpr int *p1 = &j; //p1是常量指针,指向整数j
处理类型
- 类型别名
两种定义方式:关键字typedef和别名声明
typedef double wages; // wages是double的同义词
using SI = Sales_item; //SI是Sales_item的同义词
typedef char *pstring; //: 基本数据类型为指针
const pstring cstr = 0; //cstr是指向char的常量指针
//不可以改写成const char *cstr -> 指向char常量的指针
const pstring *ps; //ps是一个指针,对象是指向char的常量指针
- aoto类型说明符
注意点:如果是常量引用/指针:auto一般会忽略掉顶层const,同时底层const则会保留下来。
int i = 0;
const int ci = i, &cr = ci;
const auto f = ci; //ci的推演类型是int, f是const int
auto &g = ci; //g是一个整型常量引用,绑定到ci
auto &h = 42; //错误:不能为非常量引用绑定字面值
const auto &j = 42; //正确:可以为常量引用绑定字面值
auto k = ci, &l = i; //k是整数,l是整型引用 -> k的值可以改变
auto &n = i, *p2 = &ci; //错误:i的类型是int而&ci的类型是const int
- decltype类型指示符
decltype(f()) sum = x; //sum的类型就是函数f的返回类型
注意点:
1)decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。
2)如果i是int,则表达式i=x的类型是int&。
自定义数据结构
库类型string、istream、ostream等都是以类的形式定义的。
预处理器:确保头文件多次包含仍能安全工作的技术。
#include -> 预处理器看到#include标记时会用指定的头文件内容代替#include
#define指令把一个名字设定为预处理变量
#ifdef当且仅当变量已定义时为真
#ifndef当且仅当变量未定义时为真
检查结果为真:执行后续操作直到#endif
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
#endif
参考资料:
- C++ primer. 第五版.