前言:本专栏记录了博主C++从初阶到高阶完整的学习历程,会发布一些博主学习的感悟、碰到的问题、重要的知识点,和大家一起探索C++这门程序语言的奥秘。这个专栏将记录博主C++语法、高阶数据结构、STL的学习过程,正所谓“万丈高楼平地起”嘛,我们话不多说,继续进行C++阶段的学习。本文我们不讲C++主线的内容,我们来拓展一下或者说整理一下我们学习时C/C++时经常会提到的一些专有名词,例如形参、实参,显式类型转换和隐式类型转换类型转换,内置类型等等。
C++的两个参考文档:
老朋友(非官方文档):cplusplus 官方文档(同步更新):cppreference
博主之前写过C++引用相关的内容,只不过那是在C++初识部分介绍的,比较简略:
【C/C++】初识C++(二):深入详解缺省参数(默认参数)函数重载、引用(重头戏)
【C/C++】初识C++(三):C++入门内容收尾——const引用,指针和引用关系梳理,inline(内联函数),nullptr替代NULL
引用是C++中一种重要的特性,它提供了对变量的别名机制,使得我们可以用不同的名称访问同一块内存空间。下面全面介绍C++引用的核心知识点。
int num = 10;
int &ref = num; // ref是num的引用特性 | 引用 | 指针 |
|---|---|---|
初始化要求 | 必须初始化 | 可以不初始化 |
可修改性 | 不能重新绑定 | 可以指向不同对象 |
空值 | 不能为空 | 可以为NULL/nullptr |
操作方式 | 直接使用 | 需要解引用(*) |
内存占用 | 通常不占额外空间 | 占用指针大小的空间 |
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}int &getMax(int &a, int &b) {
return a > b ? a : b;
}
int main() {
int x = 5, y = 10;
getMax(x, y) = 20; // 修改了y的值
}const int &ref = 42; // 合法:常量引用可以绑定到字面量void print(const std::string &str) {
// 不能通过str修改原对象
std::cout << str;
}int a = 10;
int &lref = a; // 左值引用
int &&rref = 20; // 右值引用class MyString {
public:
// 移动构造函数
MyString(MyString &&other) noexcept {
data = other.data;
other.data = nullptr;
}
};template<typename T>
void func(T&& param) { // 万能引用
// 根据传入参数决定引用类型
}
int a = 10;
func(a); // T&
func(10); // T&&int arr[5] = {1, 2, 3, 4, 5};
int (&refArr)[5] = arr; // 数组的引用int *ptr = nullptr;
int *&refPtr = ptr; // 指向指针的引用int &badFunc() {
int local = 10;
return local; // 错误!
}int &ref; // 错误:必须初始化int &ref = NULL; // 错误引用是C++强大而独特的特性,正确使用引用可以写出更高效、更安全的代码,但同时需要注意其特性和限制以避免潜在问题。
#include <iostream>
using namespace std;
int main() {
int original = 42;
int &ref = original; // 引用声明和初始化
cout << "original: " << original << endl; // 42
cout << "ref: " << ref << endl; // 42
ref = 100; // 通过引用修改值
cout << "after change:" << endl;
cout << "original: " << original << endl; // 100
cout << "ref: " << ref << endl; // 100
// 验证它们共享同一地址
cout << "Address of original: " << &original << endl;
cout << "Address of ref: " << &ref << endl; // 相同地址
}void compareRefAndPointer() {
int x = 10;
// 引用方式
int &ref = x;
ref = 20; // 直接使用
// 指针方式
int *ptr = &x;
*ptr = 30; // 需要解引用
cout << "x after ref change: " << x << endl;
cout << "x after ptr change: " << x << endl;
// 尝试重新绑定
int y = 50;
// ref = y; // 错误!不能重新绑定引用
ptr = &y; // 指针可以重新指向
}// 交换两个变量的值
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void testSwap() {
int x = 10, y = 20;
cout << "Before swap: x=" << x << ", y=" << y << endl;
swap(x, y);
cout << "After swap: x=" << x << ", y=" << y << endl;
}// 返回数组元素的引用
int &getElement(int arr[], int index) {
return arr[index];
}
void testReturnRef() {
int numbers[] = {10, 20, 30, 40, 50};
// 获取引用并修改
getElement(numbers, 2) = 100;
cout << "Modified array: ";
for (int num : numbers) {
cout << num << " ";
}
cout << endl;
// 错误示例:返回局部变量引用
/*
int &badRef() {
int local = 42;
return local; // 警告:返回局部变量引用
}
*/
}void constRefDemo() {
int a = 10;
const int &ref1 = a; // 合法
// ref1 = 20; // 错误:不能通过const引用修改
const int b = 20;
const int &ref2 = b; // 合法
// 绑定到临时对象
const int &ref3 = 30; // 合法
cout << "ref3: " << ref3 << endl;
}void printLargeObject(const string &str) {
cout << str << endl;
// str[0] = 'A'; // 错误:不能修改
}
void testConstRefParam() {
string longStr(1000, 'x'); // 大字符串
printLargeObject(longStr); // 避免拷贝
// 可以接受临时对象
printLargeObject("Temporary string");
}void rvalueRefDemo() {
int &&rref1 = 10; // 右值引用
int x = 10;
// int &&rref2 = x; // 错误:不能绑定左值
rref1 = 20; // 可以修改
cout << "rref1: " << rref1 << endl;
}class StringWrapper {
char *data;
public:
// 构造函数
explicit StringWrapper(const char *str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
// 移动构造函数
StringWrapper(StringWrapper &&other) noexcept {
data = other.data;
other.data = nullptr;
}
// 移动赋值运算符
StringWrapper &operator=(StringWrapper &&other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}
~StringWrapper() {
delete[] data;
}
void print() const {
cout << (data ? data : "null") << endl;
}
};
void testMoveSemantics() {
StringWrapper sw1("Hello");
StringWrapper sw2 = std::move(sw1); // 使用移动构造函数
cout << "After move:" << endl;
cout << "sw1: ";
sw1.print(); // 输出 null
cout << "sw2: ";
sw2.print(); // 输出 Hello
}void refArrayDemo() {
int arr[3] = {1, 2, 3};
int (&refArr)[3] = arr; // 数组的引用
refArr[1] = 20; // 修改原数组
cout << "Original array: ";
for (int n : arr) {
cout << n << " ";
}
cout << endl;
}void pointerRefDemo() {
int x = 10;
int *ptr = &x;
int *&refPtr = ptr; // 指向指针的引用
*refPtr = 20; // 修改x的值
cout << "x: " << x << endl;
int y = 30;
refPtr = &y; // 修改指针指向
*refPtr = 40;
cout << "y: " << y << endl;
}class Counter {
int count;
public:
Counter() : count(0) {}
Counter &increment() {
++count;
return *this;
}
Counter &decrement() {
--count;
return *this;
}
int get() const { return count; }
};
void testChaining() {
Counter c;
c.increment().increment().decrement();
cout << "Count: " << c.get() << endl; // 输出 1
}class Logger {
ostream &out;
public:
explicit Logger(ostream &os = cout) : out(os) {}
void log(const string &msg) {
out << msg << endl;
}
};
void testLogger() {
Logger consoleLogger;
consoleLogger.log("This goes to console");
ofstream file("log.txt");
Logger fileLogger(file);
fileLogger.log("This goes to file");
}博主展示的这些代码示例,涵盖了C++引用的各种用法,从基础到高级特性。通过实际运行这些代码,可以更深入地理解引用的工作原理和应用场景。
往期回顾:
【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办
结语:本文内容到这里就全部结束了。本文我们重新整理了C++引用和指针相关的内容。