类型分类

1. 内置类型(基本类型)

内置类型是C++语言自带的基本数据类型,主要包括以下几种:

  • 整型(Integer Types)
    • int:标准整型,通常为4字节。
    • short:短整型,通常为2字节。
    • long:长整型,通常为4或8字节(取决于平台)。
    • long long:更长的整型,通常为8字节。
    • unsigned:无符号整型,不支持负数。
    • unsigned shortunsigned longunsigned long long:对应的无符号版本。
  • 字符型(Character Types)
    • char:字符型,通常为1字节。
    • wchar_t:宽字符型,通常为2或4字节(用于表示Unicode字符)。
    • char16_tchar32_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++中使用引用,可以让我们直接访问和操作另一个变量的内存地址,而不需要通过指针的解引用操作。引用在语法上比指针更简洁,且在许多情况下更安全。

变量内存演示

上面的定义很多人看起来很吃力,那我们回想一下变量的存储。

https://cdn.llfc.club/1726452128688.jpg

上面的图表示:

  • 定义一个变量a,并且初始化为100,编译器会为变量a开辟空间,绿色的是开辟的空间,存储100,这个空间的首地址为0x2be3,也就是变量a的地址。
  • 地址大家可以裂解为门牌号,我们可以通过门牌号找到绿色的家,a是绿色的家的名字,进而取出家里的物品,数据100可以理解为物品。
  • 执行b = a,将a赋值给b后,编译器又开辟了一块空间,存储100,这个空间的首地址为0x3f2b。 打个比方,我们又创建了一个家,家的名字是b,家里也存储了100这个物品,但是这个家的地址和a的不一样。

引用内存演示

https://cdn.llfc.club/1726453490297.jpg

  • 我们同样定义了一个变量a,并为它开辟空间,存储100,空间的首地址为0x2be3
  • 我们定义了一个引用b,它是a的别名,所以b的地址和a的地址都一样,都是0x2be3

从上述图形可以看出,引用是变量的别名。

写法

引用的基本写法

// 定义变量a
int a = 100;
// 定义引用b并且指向a, b就是a的别名
int &b = a;

大家能看到,在定义引用b的时候在intb之间我们加了&符号, 这个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。

特性

  1. 必须初始化:引用在创建时必须被初始化,它必须指向某个已存在的对象。
  2. 一旦绑定,不可改变:引用一旦被初始化后,它将一直保持与其初始对象的绑定,不能改变为另一个对象的引用。
  3. 没有空引用:引用必须指向某个对象,不能存在空引用。

看下面的例子

#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新增的特性,用于为右值(即临时对象)创建引用,支持移动语义和完美转发等高级特性。
  • 右值引用的主要用途不是直接绑定到字面量或简单的右值表达式上,而是在函数参数和返回值中,以实现更高效的资源管理和更灵活的代码编写方式。

results matching ""

    No results matching ""