复合类型
引用
声明方式
int a;
int &p = a;
int &b; // 报错,引用必须被初始化
引用必须与具体对象绑定,不可以绑定到字面值或表达式的计算结果上,并且类型必须严格匹配。
int &a = 10; // error
double b = 21.3;
int &c = b; // error
指针
int a = 1;
int *p = &a;
double *e = &a; //error
指针的类型需要匹配
初始化空指针
int *p = nullptr;
int *p = 0;
int *p = NULL;
最好使用nullptr
指针的size:取决于机器是32位的还是64位的,因为指针存的是地址,所以32位大小是4B,64位是8B
void*
void*
是一种特殊指针,它可以存放任意类型的地址,不必拘泥于类型。但是不可以直接对void*
的对象进行操作(因为不知道它的类型)。
int a = 10;
double b = 1.5;
void* p = &a;
double* k = &b;
p = k;
// cout<<*p<<endl; // error,因为不可以直接操作void指针
*(double*)p = 2.5; // 使用强制类型转换
cout<<b<<endl; // 2.5
定义多个复合变量
int *p;
的基本数据类型是int,* 修饰的p。
int *p, q
中,p是指针,q是int
int *p, *q
是两个指针
指向指针的指针
int a = 10;
int *p = &a;
int **q = &p;
从右往左读,指向指针的引用
#include<iostream>
#include"swap.h"
using namespace std;
int main() {
int a = 10;
int c = 3;
int *p = &a;
int*& r = p; // r 是指向p的引用
*r = 1;
printf("a = %d, c = %d\n", a, c);
// a = 1, c = 3
r = &c; // r和p都指向c了
*p = 2;
printf("a = %d, c = %d\n", a, c);
// a = 1, c = 2
return 0;
}
}
认识r
int *p = nullptr;
int *&r = p;
在这里,从右往左读。离r最近的是&,所以r是一个引用,&的左边是一个*,所以r引用的是指针, 再左边是int,所以r引用的一个int型的指针。
const限定符
const的对象创建之后就不可以改变,必须在创建时初始化。
默认状态下,const仅在文件内有效。
编译器对const做的其实是将所有声明的词做替换,如const int a = 1;
,那么编译器就在本文件内找到所有的a,将其换为1.
extern关键字的说明
多文件共享const,不在声明时定义,在某文件定义:
对const的引用
又称常量引用
const int a = 1;
const int &r = a;
r = 2; // error
int &r2 = a; // error
初始化常量引用
int i = 1;
const int a = i;
const int b = 1;
const int c = 2 * i;
int& c = a;
当使用常量引用的适合,可以对不同类型的对象赋值。
double a = 3.14;
const int &r = a;
int &r2 = a; // error
因为在赋值时,进行了如下操作
const int temp = a;
const int &r = temp;
因为r是引用常量,所以不会对a的值进行改变,所以允许赋值。但是r2不是常量,可能会改变a的值,所以不允许赋值。
举例说明,对const的引用的作用
int a = 3;
const int r1 = a;
const int& r2 = a;
a = 0;
cout << a << " " << r1 << " " << r2;
// 0 3 0
使用常量引用可以获得一个值会随着原对象改变的引用变量,直接使用const,那么r1的值就是在赋值那一刻确定了,后续a改变,r1的值也不会改变。
const与指针
const对仅对指针限制,对原对象不限制。上面的引用也是同理
指向常量的指针,不能通过该指针修改变量的值,但是可以通过其他途径修改
const指针,不能修改指针指向的对象
int a = 3;
const int *p = &a;
int const *p1 = &a;
int* const p2 = &a;
const int* const P = &a;
int b = 2;
p = &b;
p1 = &b;
// *p1 = 1; // error
*p2 = 1;
// p2 = &b; // error
// P = &b; // error
// *P = 1; // error
const int *p
与int const *p
等价,都不可以通过p修改值(const int
==int const
)
int * const p
不可以修改p指向的对象
const int const *p
双重限制
顶层/底层const
constexpr
常量表达式 是指值不会改变且在编译过程中就可以得到计算结果的表达式
const int a = 1;
const int b = a + 1;
const int c = getValue(); // false
int d = 1; // false
constexpr和const的区别
constexpr表示在编译期就可以算出来(它所依赖的东西也是在编译期可以算出来的)。const只保证了运行时不直接被修改(但这个东西仍然可能是个动态变量)。
constexpr 指针
constexpr修饰指针时,做顶层const,仅对指针有效,对指针所指的对象无效。
int i = 3;
constexpr int* p = &i; // 常量指针
const int* q = &i; // 指向常量的指针
*p = 2;
cout << i; // 2
constexpr const int p = &i;
处理类型
类型别名
typedef和using
typedef double D;
using I = int;
auto
由编译器自动推断数据类型,可以一行声明多个变量,但是其类型要一致。
int i = 3;
auto a1 = 0, &r1 = i, *p1 = &i; // 正确,int型数、引用、指针
auto a2 = i, a3 = 3.5; // error
decltype
当希望从表达式的类型推断出当前定义变量的类型,但是不希望用该表达式来为其赋值时,使用decltype
int a = 1, *p = &a, &r = a;
decltype(a) c = 10; // type is int, value is 10;
decltype(p) pp; // 指针
decltype(r) q; // error,q是引用,必须初始化
decltype和引用
如上节的例子,
decltype(*p) t
是错误的,因为*p解引用得到的是一个指针的对象,还可以修改该对象,所以decltype(*p)
的结果是int&, 所以必须初始化t。
decltype(r + 0) t
是正确的,因为引用+0后得到的值是int
自定义数据结构
不要忘了分号
struct Name{
/*---*/
};