写在前面:
原文链接:https://blog.csdn.net/qq_21438461/article/details/131297103
_reference_wrapper
功能: 一个模板类,用来定义保存对某中对象的引用,并且其可以像普通对象一样进行复制和赋值。
结构定义:
示例:
template <typename T>
class reference_wrapper {
private:
T* ptr; // 指向引用对象的指针
public:
// 构造函数,接受一个引用,并存储其地址
reference_wrapper(T& ref) : ptr(&ref) {}
// 返回存储的引用
T& get() const {
return *ptr;
}
// 重载函数调用操作符,使其可以像引用一样使用
operator T& () const {
return *ptr;
}
};
注意:reference_wrapper 可以通过存储引用的地址(即指针)来间接地表示引用,并允许 reference_wrapper 在需要时返回原始引用。此时需要注意的时,返回原始引用并进行修改时。同样会对原始对象的值进行修改,如下:
int a = 20;
std::_reference_wrapper<int> ref(a);
int& b = ref.get();
b = 30; //此时a的值也会被修改掉
cout<< a <<endl; //输出30
成员函数
- get()
- std::reference_wrapper::operator()
- bool operator==(const reference_wrapper& x, const reference_wrapper& y)
#include <iostream>
#include <functional> // 包含std::reference_wrapper
int main()
{
int x = 10;
// std::reference_wrapper<T>::reference_wrapper(T& val)构造函数
std::reference_wrapper<int> rw(x);
// T& std::reference_wrapper<T>::get() const函数,返回包装的引用
int& y = rw.get();
std::cout << "y = " << y << std::endl; // 输出 y = 10
// T& std::reference_wrapper<T>::operator()() const运算符,返回包装的引用
rw() = 20;
std::cout << "x = " << x << std::endl; // 输出 x = 20
// std::reference_wrapper<T>::reference_wrapper(const reference_wrapper&)构造函数
std::reference_wrapper<int> rw2 = rw;
// 输出rw2所引用的值
std::cout << "rw2 = " << rw2.get() << std::endl; // 输出 rw2 = 20
// bool operator==(const reference_wrapper<T>& x, const reference_wrapper<T>& y)函数,比较两个reference_wrapper对象
if (rw == rw2) {
std::cout << "rw and rw2 are equal" << std::endl; // 输出 rw and rw2 are equal
}
// 定义一个新的int变量,并且用它来重新赋值reference_wrapper
int z = 30;
// reference_wrapper<T>& operator=(const reference_wrapper<T>& x)函数,赋值运算符
rw = std::ref(z); // 使用std::ref函数返回一个对象的引用
// 输出rw现在所引用的值
std::cout << "rw = " << rw.get() << std::endl; // 输出 rw = 30
return 0;
}
4.std::ref(t) - 这个全局函数接受一个左值引用,并返回一个封装了这个引用的 std::reference_wrapper 对象。这个函数通常用于需要引用但接口只接受值的场合。
5.std::cref(t) - 这个全局函数接受一个常量左值引用,并返回一个封装了这个引用的 std::reference_wrapper 对象。这个函数通常用于需要常量引用但接口只接受值的场;
注意事项:
-
需要使用objectsRef.get()来获取原始的引用;
原因:std::reference_wrapper
本质上是一个包装了引用的类,因此你需要通过get
方法来获取其内部封装的引用。
如果直接对std::reference_wrapper
实例调用成员接口,编译器会报错,因为std::reference_wrapper
并没有你想要调用的成员接口。这也是为什么我们需要使用get
方法的原因。 -
声明类型时不需要引用
示例: std::_reference_wrapper<int> a = obj; //此处int只表示引用的数据类型
原因:实际上创建了一个引用a,它指向obj
。你可以像使用普通引用那样使用它,但是它有一些附加的优点,例如可以存储在容器中(因为它本身是一个对象),并且可以重新绑定到其他对象。所以,尽管没有在std::reference_wrapper<object::Objects>
的声明中没有明确写出引用,但它仍然持有一个引用。 -
不能初始化为空
原因:C++中对引用初始化必不为空,_reference_wrapper同样必须与一个实际已存在的对象进行关联。
如果你需要一个可能为空的引用,那么需要引用指针或者如下:
std::optional<std::reference_wrapper<T>>
std::reference_wrapper和智能指针的区别
-
所有权 :智能指针是一种可以自动管理对象生命周期的指针,它有明确的所有权语义,例如std::unique_ptr有独占所有权,std::shared_ptr则有共享所有权。而std::reference_wrapper没有所有权语义,它只是对另一个对象的引用,当引用的对象被销毁时,std::reference_wrapper不会对此做出任何响应。
-
空值:智能指针可以为空,这对于表示可选的或者可以拥有或者不拥有对象的情况是有用的。而std::reference_wrapper不能引用空值,因为引用在C++中必须总是指向一个对象。
-
动态分配:智能指针通常用于管理动态分配的对象(即在堆上创建的对象)。这是因为动态分配的对象的生命周期必须由程序员明确管理,而智能指针通过自动删除它们所拥有的对象来帮助管理这些生命周期。而std::reference_wrapper只是引用在对象,无论对象本身是否动态分配。
std::reference_wrapper的默认转换
std::reference_wrapper有一个转换操作符,它允许std::reference_wrapper隐式转换为T&。这就是为什么你可以直接将std::reference_wrapper传递给接受int&参数的函数的原因。
//1.
void func(int& x) { /* ... */ }
std::reference_wrapper<int> ref = /* ... */;
func(ref); // 自动转换为 int&
//2.
int& x = ref; // 自动转换为 int&
但是需要注意以下几种情况:
- 需要访问引用对象成员的时候;
- 需要明确表示你正在访问引用对象时;
- 不支持隐式转换的场合:有些编程场景(如模板编程)可能不支持隐式转换。
此时必须使用get() 方法,get()也是更安全明确的使用方法。
std::reference_wrapper的使用场景
- 用于容器中的引用
在C++中,我们通常不能在STL容器(如std::vector,std::list等)中存储引用,因为引用不能被重新赋值。然而,当我们需要在容器中存储对象的引用,以避免对象的复制。这时,std::reference_wrapper就派上用场了。
示例:
//初始化一个包含对int类型a、b、c引用的vector
int a = 1, b = 2, c = 3;
std::vector<std::reference_wrapper> vec{a, b, c};
//遍历vec,可以通过修改对应元素完成对a、b、c的修改操作。
for(auto &ref : vec) {
++ref;
}
std::reference_wrapper的注意事项
-
生命周期管理
std::reference_wrapper本身并不拥有它所引用的对象,因此,我们必须确保在使用std::reference_wrapper的整个生命周期中,它所引用的对象是存在的。
解决办法:a确保引用生命周期大于实体店std::reference_wrapper;
b在对象生命周期结束前更新std::reference_wrapper的引用; -
引用的可变性
const std::reference_wrapper ref = x //此时const限制修改ref引用的对象,但是不限制对x进行修改,const修饰的是引用本身。 -
引用的拷贝行为
std::reference_wrapper(C++引用包装器)提供了一种方式来拷贝和赋值引用。
在C++中,引用本身不能被重新赋值,一旦一个引用被初始化,它就会一直引用同一个对象,不能改变引用的对象。
std::reference_wrapper的拷贝构造函数和拷贝赋值运算符都会拷贝引用,而不是拷贝引用的对象。这意味着,如果我们拷贝一个std::reference_wrapper,新的std::reference_wrapper将引用同一个对象。
示例:
int x = 10;
int y = 20;
std::reference_wrapper<int> ref1 = x;
std::reference_wrapper<int> ref2 = ref1; // ref2现在引用x
ref2 = y; // ref2现在引用y
std::reference_wrapper在Qt中的应用
-
在Qt信号和槽机制中的应用
在Qt中,信号和槽(Signal and Slot)是一个重要的机制,它允许在不同的对象之间进行通信。然而,当我们想要通过信号和槽传递引用时,就会遇到一些问题。这是因为Qt的信号和槽机制默认是通过值传递参数的。这时,std::reference_wrapper就派上了用场。
std::reference_wrapper可以被用作一个包装器,使得我们可以通过Qt的信号和槽机制传递引用。 -
在Qt事件处理中的应用
Qt的事件处理机制是其核心功能之一,它允许我们对各种用户输入和系统事件进行响应。然而,当我们想要在事件处理函数中修改某个对象的状态时,我们通常需要将这个对象作为函数的参数传入。这时,std::reference_wrapper就可以派上用场。