在C++中,引用(reference)是一种别名,可以用来避免值传递带来的开销,并直接操作原始变量。以下是一些引用方法的使用案例,包括引用作为函数参数、返回值和类成员。
引用作为函数参数
通过引用传递参数可以避免复制参数,并且可以在函数内部修改原始变量。
#include <iostream>
void increment(int &n) {
n++;
}
int main() {
int a = 5;
increment(a);
std::cout << "Value of a after increment: " << a << std::endl; // Output: 6
return 0;
}
引用作为函数返回值
函数可以返回一个引用,从而可以直接操作原始变量。
#include <iostream>
int& getElement(int arr[], int index) {
return arr[index];
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
getElement(arr, 2) = 10; // 修改原数组
std::cout << "Value of arr[2]: " << arr[2] << std::endl; // Output: 10
return 0;
}
常量引用
常量引用用于保护引用的变量不被修改,同时避免了传值的开销。
#include <iostream>
void myprint(const int &n) {
std::cout << "Value: " << n << std::endl;
}
int main() {
int a = 5;
myprint(a);
return 0;
}
引用作为类成员
引用可以作为类的成员变量来使用,不过需要在初始化列表中进行初始化。
#include <iostream>
class Example {
private:
int &ref;
public:
Example(int &r) : ref(r) {}
void set(int value) {
ref = value;
}
void print() const {
std::cout << "Value: " << ref << std::endl;
}
};
int main() {
int a = 5;
Example ex(a);
ex.print(); // Output: 5
ex.set(10);
ex.print(); // Output: 10
std::cout << "Value of a: " << a << std::endl; // Output: 10
return 0;
}
头文件 Example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <iostream>
class Example {
private:
int &ref;
public:
Example(int &r);
void set(int value);
void print() const;
};
#endif // EXAMPLE_H
源文件 Example.cpp
#include "Example.h"
Example::Example(int &r) : ref(r) {}
void Example::set(int value) {
ref = value;
}
void Example::print() const {
std::cout << "Value: " << ref << std::endl;
}
主程序文件 main.cpp
#include "Example.h"
int main() {
int a = 5;
Example ex(a);
ex.print(); // Output: 5
ex.set(10);
ex.print(); // Output: 10
std::cout << "Value of a: " << a << std::endl; // Output: 10
return 0;
}
引用用于交换函数
引用可以用于实现交换两个变量值的函数。
#include <iostream>
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
swap(x, y);
std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
return 0;
}
- 引用介绍
(1)引用的经典案例:实现swap函数实战
(2)引用定义和识别的关键:&符号,注意这里和取地址一毛钱关系都没有
(3)引用符号(注意我没说变量)在定义时必须同时初始化,以后不能再另外赋值,只能使用 - 引用和指针的对比
(1)指针在C和C++中都有,且使用方法和实现本质完全相同;引用只有C++可用
(2)引用可以理解为功能弱化、安全性增强的低配版指针
(3)引用能做的事指针都能做,但指针能做的事儿引用不一定能做
(4)引用是它指向变量的“别名”,这个是从引用的使用效果角度讲的,对熟悉指针的人反而不好理解“别名”这个词
(5)引用比指针弱的地方就是一个引用定义时绑定了一个变量,后面没法改了
(6)引用比指针强的地方也是没法改,所以不存在"野指针"问题,更安全
(7)引用主要用在函数传参和返回值
引用的本质剖析
- 引用可以加const修饰
(1)const int &b = a; 表示b是a的const别名,无法通过b修改a了
(2)主要用在函数形参中,告诉大家该函数内部不会修改实参的值。用在某些时候我们有一个非const类型的变量,但是我们在某个函数调用的过程中,不希望变量的值在函数内部被修改,这时候就可以用const引用来传参。
头文件 StringCompare.h
#ifndef STRINGCOMPARE_H
#define STRINGCOMPARE_H
#include <string>
class StringCompare {
public:
static int compare(const std::string &str1, const std::string &str2);
};
#endif // STRINGCOMPARE_H
源文件 StringCompare.cpp
#include "StringCompare.h"
#include <cstring> // 包含 strcmp 函数的头文件
int StringCompare::compare(const std::string &str1, const std::string &str2) {
return strcmp(str1.c_str(), str2.c_str());
}
主程序文件 main.cpp
#include <iostream>
#include "StringCompare.h"
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "hello";
int result1 = StringCompare::compare(str1, str2);
int result2 = StringCompare::compare(str1, str3);
std::cout << "Comparison result (str1 vs str2): " << result1 << std::endl; // Output: < 0 (negative value)
std::cout << "Comparison result (str1 vs str3): " << result2 << std::endl; // Output: 0
return 0;
}
- 引用和sizeof运算符
(1)sizeof引用得到的不是引用本身的大小,而是引用指向的目标变量的大小
(2)在struct或class中定义一个引用,再sizeof整个struct或class就会不一样
头文件 TestSize.h
#ifndef TESTSIZE_H
#define TESTSIZE_H
class TestSize {
public:
static void printSize(int &ref);
static void printArraySize(int (&arr)[5]);
};
#endif // TESTSIZE_H
源文件 TestSize.cpp
#include "TestSize.h"
#include <iostream>
void TestSize::printSize(int &ref) {
std::cout << "Size of reference: " << sizeof(ref) << " bytes" << std::endl;
std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;
}
void TestSize::printArraySize(int (&arr)[5]) {
std::cout << "Size of array: " << sizeof(arr) << " bytes" << std::endl;
std::cout << "Size of array element: " << sizeof(arr[0]) << " bytes" << std::endl;
std::cout << "Number of elements in array: " << sizeof(arr) / sizeof(arr[0]) << std::endl;
}
主程序文件 main.cpp
#include <iostream>
#include "TestSize.h"
int main() {
int a = 10;
int arr[5] = {1, 2, 3, 4, 5};
std::cout << "Testing size of reference and int:" << std::endl;
TestSize::printSize(a);
std::cout << "\nTesting size of array:" << std::endl;
TestSize::printArraySize(arr);
return 0;
}
typedef struct {
int age;
int &age1;
char sex;
char &sex1;
double height;
double &height1;
} Person;
Person *per;
std::cout << "Size of int age: " << sizeof(int) << " bytes" << std::endl;
std::cout << "Size of char sex: " << sizeof(char) << " byte" << std::endl;
std::cout << "Size of double height: " << sizeof(double) << " bytes"
<< std::endl;
std::cout << "Size of Person struct: " << sizeof(Person) << " bytes"
<< std::endl;
std::cout << "sizeof(per->age): " << sizeof(per->age) << " bytes"
<< std::endl;
std::cout << "sizeof(per->age1): " << sizeof(per->age1) << " bytes"
<< std::endl;
std::cout << "sizeof(per->height): " << sizeof(per->height) << " bytes"
<< std::endl;
std::cout << "sizeof(per->height1): " << sizeof(per->height1) << " bytes"
<< std::endl;
std::cout << "sizeof(per->sex): " << sizeof(per->sex) << " bytes"
<< std::endl;
std::cout << "sizeof(per->sex1): " << sizeof(per->sex1) << " bytes"
<< std::endl;
4 (age) + 4 (age1) + 1(sex) + 1 (sex1) + 8 (height) +8(height1) = 26 字节
但是,实际上结构体 Person 的大小是 48 字节,这可能涉及到了编译器的内存对齐和填充策略。编译器通常会在结构体的成员之间插入填充字节,以确保每个成员都按照特定的对齐方式放置,从而提高访问速度和效率。因此,结构体的大小可能大于所有成员大小的总和。
引用成员的大小:
对于引用成员 per->sex1,尝试获取其大小通常是不允许的,因为引用在 C++ 中是一种语义上的概念,而不是真正占用内存空间的实体。因此,sizeof(per->sex1) 的结果可能会出现编译器报错或返回不可预测的值。
- 引用的本质是const指针
(1)int &b = a; 类似于 int * const b = &a;
(2)C++标准并没有规定引用是否占用内存空间,但是大多数编译器都把引用实现为const指针,所以大部分编译器中引用也是要占内存空间的
(3)引用是天然const的,所以定义时必须初始化指向变量,否则就没意义了
(4)引用本质是指针,是地址,所以才能实现传址调用的效果
总结:引用就是指针在定义时增加了把指针变量本身const化
总结
理解引用本质,熟悉使用方法
学习记录,侵权联系删除。
来源:朱老师物联网大课堂