2. 变量和基本类型
2.1 基本内置类型
- 每种数据类型都有一个最小尺寸。
- 类型选用:
int
、double
。 - 无符号整数的运算(比如相减),出现负数时,会导致结果特别大。
- 整数和浮点数字面值:容量够用就行。
字符和字符串赋值:单引号字符,双引号字符串,且带\0
。 - 转义字符以
\
开始,\1234
看3位数字;\x1234
看所有数字。 - 整数、浮点数,指定类型:后缀U、L、LL、F
字符、字符串,指定类型:前缀u、U、L、u8
2.2 变量
- 初始化只是借用了
=
符号,但初始化不等同于赋值。(举例:引用的初始化) - 函数外,默认初始化为0;函数内,内置类型不进行默认初始化(除非自己写)。
- C++是一种静态类型语言,在编译阶段检查类型。
- 作用域:内部变量会屏蔽外部同名变量。除非使用作用域运算符
::
。
2.3 复合类型
- 引用:不是对象。只是为一个已经存在的对象另外起一个名字。
引用仅能在初始化时绑定对象,且之后无法更改。
初始化后,一切操作对引用的操作,都是在绑定的对象上进行操作。 - 引用绑定的东西,必须是个对象。不能是表达式,不能是函数返回值。
- 指针:是对象。
指针类型必须与所指对象类型匹配,否则需类型转换(void*
除外)。 - C++中,空指针使用关键字
nullptr
。所有指针建议都初始化为0。 - 指向指针的指针:没啥说的,直接套就行。
指向指针的引用:int *p; int *&r=p; r=&i; *r=0;
。
2.4 const限定符
- const对象一旦创建不可更改,故必须进行初始化。
- const对象仅文件内有效。除非使
extern
进行声明。 - 对const的引用:通过引用不能修改。对象可以是非常量。
- 对const的引用的初始化:(与引用的初始化完全不同)
(a) 可以是表达式,可以是函数返回值;
(b) 可引用非const的对象。 - 若对象是常量,引用只能是对const的引用。
- 指向const的指针:通过指针不能修改。对象可以是非常量。
- 若对象是常量,指针只能是指向const的指针。
- const指针:指针本身不能修改。所指的对象可以修改。
- 顶层const:本身是个常量。
底层const:指针所指、或引用所绑定的对象是个常量。
声明引用的const都是底层const。
int*
能赋值给const int*
,反之不可以。 - 常量表达式:值不会改变,且在编译时就有结果的表达式。
(普通函数、普通int只能在执行时才有结果。)
(constexpr函数、constexpr变量、const变量在编译时就有结果。)
constexpr类型:由编译器验证变量的值是否是一个常量表达式。
声明为constexpr的变量一定是一个常量,必须用常量表达式进行初始化。
constexpr函数:特殊的函数。它可以在编译时计算出结果。 - constexpr把它所定义的对象置为了顶层const。
const int *p = nullptr;
,p是一个指向int const的指针
constexpr int *q = nullptr
,q是一个指向int的const指针
constexpr指针既可以指向常量,也可以指向非常量。
2.5 处理类型
-
typedef
定义类型别名。typedef double wages; // wages是double的同义词 typedef wages base, *p; // base是double的同义词,p是*double的同义词
-
using
别名声明。
using SI = Sales_item;
-
指针与类型别名:
typedef char *pstring;
不能单纯的把pstring换成char*
因为pstring是char*
类型
所以下面两种写法是等价的:
char *const pstring = 0;
const pstring cstr = 0;
cstr是指向char的常量指针。 -
auto说明符:让编译器分析表达式所属类型。
所以显然,auto定义的变量必须有初始值。
auto item = val1 + val2;
auto语句在一行里声明多个变量,这些变量类型都一样。 -
auto一般会忽略顶层const,保留底层const。
-
auto
初始化一个变量时,赋值号:
右边是int ,则左边是int 。
右边是int&,则左边是int 。
右边是int*,则左边是int*。
右边是const int ,则左边是const int 。
右边是const int&,则左边是const int 。
右边是const int*,则左边是const int*。 -
const auto
初始化一个变量时,赋值号:
右边是int ,则左边是const int 。
右边是int&,则左边是const int 。
右边是int*,则左边是const int*。
右边是const int ,则左边是const int 。
右边是const int&,则左边是const int 。
右边是const int*,则左边是const int* const。 -
auto &
初始化一个变量,原来的初始化规则仍然适用。 -
对以上规则进行总结:
int i = 0, &r = i; const int ci = i, &cr = ci; auto a = r; // a是一个整数 auto b = ci; // b是整数(忽略顶层const) auto c = cr; // c是整数(忽略顶层const,使用引用的对象的类型) auto d = &i; // d是整型指针(i不是const) auto e = &ci; // e是指向整数常量的指针(保留底层const) const auto f = ci; // ci的推演类型是int,f是const int auto &g = ci; // g是一个整型常量引用,绑定到ci auto &h = 42; // 报错:不能为非常量引用绑定字面值 const auto &j = 42; // 正确:可以为常量引用绑定字面值
-
decltyoe类型指示符
选择并返回操作数的数据类型,但编译器不计算表达式的值。decltype (f()) sum = x; // 函数f的返回类型就是sum的类型
-
如果decltype使用表达式是个变量,则返回该变量的类型。
const int ci = 0, &cj = ci; decltype(cj) x = 0; // x是const int decltype(cj) y = x; // y是const int& decltype(cj) z; // z是const int&,报错:必须初始化
-
decltype和引用:decltype的结果可能是引用类型
如果表达式内容是解引用操作,则decltype得到引用类型。int i = 42, *p = &i, &r = i; decltype(r) a; // a是int& decltype(r+0) b; // b是int(未初始化) decltype(*p) c; // c是int&,报错:必须初始化 // 这里的*是解引用符,*p是解引用操作 // 结果是int&,不是int,也不是int*。
-
对于decltype表达式来说,变量名加不加括号有很大不同。
如果不加括号,则是变量类型。
如果加上括号,则被当成表达式,此时将会得到引用类型。
另外:赋值是会产生引用的一类典型表达式
也就是说:如果i是int,则表达式i=x
的类型是int&。decltype((i)) d; // d是int&。报错:必须初始化。 decltype( i ) e; // e是int(未初始化) decltype(a=b) d = a; // d是int&,和a绑定
2.6 自定义数据结构
- struct定义结构体,定义之后要加分号。
原因:因为类的定义之后可以进行对象的定义。 - 创建类对象时,可以为数据成员提供初始值。
- C++11规定,可以为数据成员提供一个类内初始值,
对类内初始值的限制:放在花括号里,或放在等号右边。但不能使用圆括号。 - C++中,字符串需要包含头文件
#include <string>
。
操作有>>、<<、==等。(C语言里只能用strcpy()和strcpy()函数) - 类一般都不在函数体内,通常定义在头文件中。
例:库类型string在名为string的头文件中定义。
例:把Sales_data类定义在名为Sales_data.h的头文件中。 - 头文件中一般包含只能被定义一次的实体,如类、const、constexpr变量等。
- 编译预处理:
#include
。 - 用
<>
表示已有的头文件,用""
表示自己写的头文件。 - 头文件保护符:
#ifndef
、#define
、#endif
。