类型分类
1. 内置类型(基本类型)
内置类型是C++语言自带的基本数据类型,主要包括以下几种:
- 整型(Integer Types)
int
:标准整型,通常为4字节。short
:短整型,通常为2字节。long
:长整型,通常为4或8字节(取决于平台)。long long
:更长的整型,通常为8字节。unsigned
:无符号整型,不支持负数。unsigned short
、unsigned long
、unsigned long long
:对应的无符号版本。
- 字符型(Character Types)
char
:字符型,通常为1字节。wchar_t
:宽字符型,通常为2或4字节(用于表示Unicode字符)。char16_t
、char32_t
:用于表示UTF-16和UTF-32编码的字符。
- 浮点型(Floating Point Types)
float
:单精度浮点型,通常为4字节。double
:双精度浮点型,通常为8字节。long double
:扩展精度浮点型,通常为8字节或16字节(取决于平台)。
- 布尔型(Boolean Type)
bool
:布尔型,表示真(true)或假(false)。
2. 复合类型
复合类型是由内置类型或其他复合类型组合而成的类型,主要包括以下几种:
- 数组(Array)
- 一组相同类型的数据元素,可以通过索引访问。
- 结构体(Struct)
- 一种用户定义的数据类型,可以包含不同类型的数据成员。
- 联合体(Union)
- 与结构体类似,但所有成员共享同一内存位置,只有一个成员可以在任何给定时间存储值。
- 枚举(Enum)
- 一种用户定义的类型,用于定义一组命名的整型常量。
- 类(Class)
- C++的面向对象编程特性,允许定义包含数据和成员函数的复杂数据类型。
- 指针(Pointer)
- 指向其他类型的内存地址,可以用来动态分配内存和实现复杂的数据结构。
- 引用(Reference)
- 对现有变量的别名,提供了对变量的另一种访问方式。
引用类型
定义
C++ 中的引用类型是一种复合类型,它是对另一个变量的别名。在C++中使用引用,可以让我们直接访问和操作另一个变量的内存地址,而不需要通过指针的解引用操作。引用在语法上比指针更简洁,且在许多情况下更安全。
变量内存演示
上面的定义很多人看起来很吃力,那我们回想一下变量的存储。
上面的图表示:
- 定义一个变量a,并且初始化为100,编译器会为变量a开辟空间,绿色的是开辟的空间,存储100,这个空间的首地址为
0x2be3
,也就是变量a的地址。 - 地址大家可以裂解为门牌号,我们可以通过门牌号找到绿色的家,a是绿色的家的名字,进而取出家里的物品,数据100可以理解为物品。
- 执行
b = a
,将a赋值给b后,编译器又开辟了一块空间,存储100,这个空间的首地址为0x3f2b
。 打个比方,我们又创建了一个家,家的名字是b,家里也存储了100这个物品,但是这个家的地址和a的不一样。
引用内存演示
- 我们同样定义了一个变量a,并为它开辟空间,存储100,空间的首地址为
0x2be3
- 我们定义了一个引用b,它是a的别名,所以b的地址和a的地址都一样,都是
0x2be3
从上述图形可以看出,引用是变量的别名。
写法
引用的基本写法
// 定义变量a
int a = 100;
// 定义引用b并且指向a, b就是a的别名
int &b = a;
大家能看到,在定义引用b
的时候在int
和b
之间我们加了&
符号, 这个int &
表示的就是b是int类型的引用变量。
这里再提前告诉大家一个方法查看a和b地址是否相同, 当我们想输出a和b的地址的时候,只需要在a和b前加&
即可输出他们的地址
std::cout << "a的地址为:" << &a << std::endl;
std::cout << "b的地址为: " << &b << std::endl;
上面输出
a的地址为:0x7ff65cae3000
b的地址为: 0x7ff65cae3000
可以看到a和b的地址相同。也证明了a和b是指向同一个地址空间。所以我们修改a的值,b的值也会变
// 定义变量a
int a = 100;
// 定义引用b并且指向a, b就是a的别名
int &b = a;
// 输出a和b的值
std::cout << "a的值:" << a << std::endl;
std::cout << "b的值: " << b << std::endl;
// a和b是指向同一个变量。所以我们修改a的值,b的值也会变
a = 200;
std::cout << "修改a的值后,a和b的值分别为:\n" << a << std::endl;
std::cout << b << std::endl;
程序输出如下
a的值:100
b的值: 100
修改a的值后,a和b的值分别为:
200
200
可以看到修改了a的值,b也跟着变化了,接下来我们修改b的值
// 修改b的值,a的值也会变
b = 300;
std::cout << "修改b的值后,a和b的值分别为:\n" << a << std::endl;
std::cout << b << std::endl;
程序输出
修改b的值后,a和b的值分别为:
300
300
可以看到b的值修改了,a的值也变化了
// 定义c,存储a的值
int c = a;
std::cout << "c的值:" << c << std::endl;
// 修改c的值
c = 400;
std::cout << "修改c的值后,c为:" << c << std::endl;
std::cout << "修改c的值后,a和b的值分别为:\n" << a << std::endl;
std::cout << b << std::endl;
程序输出如下
修改c的值后,c为:400
修改c的值后,a和b的值分别为:
300
300
可以看到c为a的副本,修改c不影响到a和b。
特性
- 必须初始化:引用在创建时必须被初始化,它必须指向某个已存在的对象。
- 一旦绑定,不可改变:引用一旦被初始化后,它将一直保持与其初始对象的绑定,不能改变为另一个对象的引用。
- 没有空引用:引用必须指向某个对象,不能存在空引用。
看下面的例子
#include <iostream>
int main() {
int a = 100;
int &b = a; // b是a的引用
std::cout << "a = " << a << ", b = " << b << std::endl; // 输出: a = 100, b = 100
b = 200; // 更改b的值也会更改a的值
std::cout << "a = " << a << ", b = " << b << std::endl; // 输出: a = 200, b = 200
// int c = 300;
// 表示修改b的值为c的值
// b = c;
return 0;
}
注意事项
- 引用主要用于函数参数和返回值,以及类的成员变量等场景,以提供对原始数据的直接访问,从而提高程序的效率和可读性。
- 引用可以是
const
的,这表示你不能通过引用来修改它所指向的对象的值。 - 引用在内部实现上通常是通过指针来实现的,但它们在语法和用途上与指针有显著的不同。引用提供了更直观、更安全的访问方式。
左值引用和右值引用
在C++中,左值(lvalue
)和右值(rvalue
)是表达式的两种基本分类,它们决定了表达式的结果在内存中的位置和状态。左值通常指的是具有持久状态的对象,它们有明确的内存地址,可以被多次赋值。而右值通常是临时的、没有持久状态的值,它们通常没有内存地址,或者其内存地址在表达式结束后就变得无效。
C++11引入了右值引用(rvalue reference
),用T&&
表示,作为对左值引用(lvalue reference
,用T&
表示)的补充。这一特性极大地增强了C++的表达能力,特别是在资源管理和性能方面。
左值引用
左值引用是C++98就有的特性,它允许我们为已存在的对象创建一个别名。左值引用必须被初始化为一个左值,即一个具有持久状态的对象。
int a = 10;
int& b = a; // b是a的左值引用
右值引用
右值引用是C++11新增的特性,它允许我们为右值(即临时对象或即将被销毁的对象)创建一个引用。这样,我们就可以对右值进行更复杂的操作,比如移动语义(move semantics)。
int&& c = 20; // c是整数字面量20的右值引用(但这种情况不常见,通常用于函数参数或返回值)
std::string foo() {
return std::string("Hello, World!"); // 返回的临时字符串是一个右值
}
std::string &&d = foo(); // d是foo()返回的临时字符串的右值引用
但请注意,直接绑定一个右值到右值引用(如int&& c = 20;
)并不是右值引用的主要用途。右值引用的主要用途是作为函数参数(实现移动语义)和返回值(允许链式调用等)。
移动语义和完美转发
右值引用的引入主要是为了支持移动语义(move semantics),它允许我们在对象被销毁前“窃取”其资源(如动态分配的内存、文件句柄等),而不是进行深拷贝。这可以显著提高性能,特别是在处理大型对象或容器时。
完美转发(perfect forwarding)是另一个与右值引用相关的概念,它允许我们将参数原封不动地传递给另一个函数,无论是左值还是右值。这通过模板和std::forward
函数实现。
总结
- 左值引用(
T&
)是C++98就有的特性,用于为已存在的对象创建别名。 - 右值引用(
T&&
)是C++11新增的特性,用于为右值(即临时对象)创建引用,支持移动语义和完美转发等高级特性。 - 右值引用的主要用途不是直接绑定到字面量或简单的右值表达式上,而是在函数参数和返回值中,以实现更高效的资源管理和更灵活的代码编写方式。