0%

C++变量和基本类型

第二章——变量和基本类型

说在前面

C++这个板块是我在学习了C之后才学的,因此我只会记录一些重要的或者在C中没有的东西

C++板块是基于C++primer的方案学习的

1、基本内置类型

  • bool类型取值只为真(1)和假(0)

  • 当一个算术表达式中既有无符号数(unsigned)又有int型时,int型的值会转换成无符号数。且值大于0时会正常显示,小于零时会异常显示(当int为4字节时,值为2^32 + 计算结果(负数))也就是4294967295加上

    (-n)。

指定字面值类型:

  • 字符和字符串字面值
前缀 含义 代表类型
u Unicode16字符 char16_t
U Unicode32字符 char32_t
L 宽字符(2字节) wchar_t
u8 Unicode8字符 char
  • 整型字面值
后缀 最小匹配类型
u or U unsigned
l or L long
ll or LL long long
  • 浮点型
后缀 代表的类型
f or F float
l or L long double

2、变量

  • 一种初始化字符串的方法

    1
    std::string a("test");

    字符串变量名a,内容为test

3、对象

通常情况下,对象是指一块能存储数据并具有某种类型的内存空间

关于初始化的问题

在学C的时候我自己也是一直把初始化理解成赋值,但是也一直感觉怪怪的,特别是对于指针变量的初始化,感觉明显是不符合“赋值”这一操作的,今天在阅读书籍时书中也指出了初始化≠赋值,但是具体的区别目前我也不能讲的很明白,学到后面如果搞懂了再说吧。

  • 定义在函数体内部的变量将不被初始化。

列表初始化

用花括号来初始化变量,在C++中以下四种初始化都是正确的:

1
2
3
4
int a =1;
int a ={1};
int a(1);
int a{1};

4、标识符

虽然感觉很怪异,但是int _=1;是合法的。

5、复合类型变量

引用

为对象起另外一个名字,同时引用必须被初始化。

1
2
3
int a(1);
int &b=a;
b=2;//等同于 a=2;
  • 引用并非对象,可以只是理解为一个已存在变量的别名。
  • 一旦使用引用,引用对象将和引用的初始化变量绑定,无法更改,因此引用必须初始化。
  • 引用只能绑定在对象上,而不能与某个字面值或某个计算结果绑定

指向指针的引用:

1
2
3
4
5
int i=1;
int *p;
int *&r=p; //注意这里是*&r
r=&i;
*r=0;

指针

规则与C基本一样,不再赘述。

6、const 限定符

  • 作用:定义一个变量后,它的值不能再被改变

    因此,const的对象必须初始化。

  • 当多个文件出现了同名的const变量时,视作分别定义了独立的变量,也就是说默认情况下const对象只在本文件内有效。(当然也可以使用extern关键字,具体可见一些关键字 | 小董的BLOG (gitee.io)

    对于使用extern,如果要在多个文件之间共享const对象,则在定义该对象时也必须添加extern

对const对象的引用

1
2
3
const int a=10;
const int &b1=a;//对
int &b2=a;//错,引用的类型必须与引用对象类型一致

假设第三行操作合法,则可以通过b2改变a的值,这显然是不正确的

  • 若初始化引用的对象为表达式,只要该表达式的结果能转化成引用的类型即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int a=10;
    const int &b=a;
    //允许将const int型引用绑定到普通的int对象上(其他普通变量也可以视情况转化)

    //因此,可以有以下操作
    int a=10;
    int &b1=a;
    const int &b2=a;
    //可以通过b1改变a的值,但不允许通过b2改变a的值

指向const对象的指针和const指针

1
2
3
4
5
//指向const对象的指针
const int a=10,b=20;
int *p=&a;
*p=b; //错误,不可修改p指向的对象的值
p=&b; //合法,并没有更改a的值,只是更改了一般指针p的值

说明一下,上述中的int *p=&a;也可以改为const int *p=&a;且二者完全一致,原因与后文中的顶/底层const有关

1
2
3
4
5
//const指针
int a=10,b=20;
int *const p=&a;
p=&b; //错误,const指针一旦初始化便会一直指向初始化时的对象
*p=b; //正确,因为指针指向的值为普通int型,可以改变

将指针本身定义为const型(不太好用语言描述感觉),即一旦初始化完成,该指针便无法再指向其他对象。

下面还是一个const指针的说明:

1
2
3
4
//const指针
const int a=10,b=20;
const int *const p=&a;
//此时p和*p都是无法改变的

当使用const指针指向的值也是const类型时,const指针本身必须再次使用一次const,因为要保持类型的一致

顶/底层const

对于指针,指针本身是一个对象,而它也指向了另一个对象,因此用顶层const表示指针本身是个const类型,用底层const表示指针所指的对象时一个const型。一般来说,顶层const可以表示任意对象。

  • 当const + 类型 时,意为使这个类型的值为常量,如

    1
    const int *p;

    定义一个指针变量,该变量所指的值为int型,因此该值(*p)不可被修改,但是p可以被修改。

  • 当const + 变量 时,意为使该变量为常量,如:

  • ```c++
    int* const p;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    指针变量p为常量,因此其指向的地址无法被改变,但是地址中的值(*p)可以被改变。

    ## 7、constexpr类型

    * 常量表达式:指值不会改变**并且在编译过程就能得到结果的表达式**;字面值、用常量表达式初始化的const对象也是常量表达式。

    ```c
    const int i = 10; //字面值是常量表达式
    const int j = i+1; //j是常量表达式
    int k = i; //k不是const对象,所以k不是是常量表达式
    const int m = get_val(); //m的值直到运行时才能获得,所以m不是常量表达是

    从上面我们可以看出,使用const进行声明时,我们需要人为的验证赋给const对象的初始值是不是常量表达是,在复杂系统中,有时候很难分辨,而使用constexpr进行声明时,可以由编译器来验证变量是不是常量表达式

1
2
3
4
5
6
7
8
9
10
11
12
constexpr int func(int n) {
return n;
}
int main()
{
int n=10;
const int m=10;
constexpr int i = func1(10);//正确,i是一个常量表达式
constexpr int j = func1(n);//错误,n为变量,不是字面值
constexpr int k = func1(m+1);//正确,k是一个常量表达式
return 0;
}

const和constexpr的区别

  • 对于修饰对象来说,const并未区分出编译期常量和运行期常量,constexpr限定在了编译期常量
  • 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
  • constexpr将它所定义的对象置为顶层const
1
2
3
4
const int*p1=nullptr;  			//p1是一个指向常量的指针
constexpr int*p2=nullptr; //p2是一个指向整数的常量指针
constexpr const int*p3=nullptr;
//p3是一个指向常量的常量指针,由于constexpr只对指针有效,需要再加一个底层const才可以限制指针指向的对象

8、处理类型

(1)类型别名

类型别名是一个名字是某种类型的同义词。有两种办法可用于定义类型别名:

  • 使用关键字typedef,详见一些关键字 | 小董的BLOG

  • 使用别名声明:

    1
    using INT =int;

类型别名与指针、常量

有以下代码:

1
2
3
4
5
using abc=char*;
char a=10;
const abc p=&a;//此处等同于char *const p=&a;而不是 const char *p=&a;
//也就是说此处是常量指针,而不是指向常量的指针,因此:
*p=20;//合法

感觉可以直接理解为这里的const为顶层const型

(2)auto类型说明符

这里auto和C中用法有所出入,在C++11新标准中,auto用于让编译器通过初始值来推算变量的类型

1
2
int a=1,b=2;
auto c=a+b;

此时c的类型就为int

  • auto定义的变量必须有初始值
  • 参与初始化的各对象类型必须相同,否则会报错

在复合类型与常量参与的情况下:

1
2
3
4
5
const int a=1; &a1=a;
auto b=a; //b为int型
auto c=a1;//c为int型
auto d=&a;//d为指向常量的指针
const auto e=a; //顶层const需专门声明
  • auto一般会忽略顶层const

  • 对一个常量对象取地址是一种底层const

(3)decltype类型指示符

当我们定义一个变量时希望用一个表达式返回的类型定义该变量,但是又不想用该表达式的结果的值初始化变量,为满足这一要求,C++11新标准引入了decltype,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式的类型,却不实际计算它的值。

1
2
int a=1;
decltype(a) b=2;

decltype()内的参数可以为变量、函数、甚至引用

decltype和引用:

1
2
3
4
5
int a=1,&a1=a,*p=&a;
int b=2;
decltype(a1) c=b;//c为int&,也就是引用类型
decltype(a1+0) d=1; //d为int型,原因是a1+0的结果为int型
decltype(*p) e=b; //*p的值按道理来说是1,书上讲这里讲为int&型,也就是与c一样

当decltype传入的参数添加上一对以上的括号后,例如decltype((a)),结果永远为引用