在指针与通过参考传递指针传递指针上使用Reference_wrapper有好处吗?
Is there a benefit to using reference_wrapper on a pointer vs passing the pointer by reference?
在qt中,当用户单击它们时,我使用 QAction
对象启用/禁用菜单按钮。在我看来,我在每种情况下都编写相同的功能,因此我将其变成了一个私有哈希表的功能,该功能可以控制菜单按钮的启用和禁用。换句话说,我的哈希表看起来像std::unordered_map<bool,QAction*> table
。布尔值是密钥,而对象是我要更新的值。所以我写了以下功能:
void updateButton(QAction *currentButton)
{
if(!table.empty())
{
// Enable the last menu item you clicked on and disable the current menu item.
table[false]->setEnabled(true);
currentButton->setEnabled(false);
table[false] = currentButton;
}
else
{
// Menu item was clicked for the first time
currentButton->setEnabled(false);
table[false] = currentButton;
}
}
因此,每当我单击菜单项时。我将此功能称为顶部:
void on_action_menuItem1_triggered()
{
updateButton(ui->actionDoAction);
...
}
然后,我意识到我是按价值传递指针的。而且,由于我有很多按钮,因此我必须管理,如果我可以避免使用任何副本。在这一点上,我完全忘记了我可以通过引用通过指针来执行QAction *¤tButton
。所以我开始环顾四周,发现STD :: Reference_wrapper。因此,我将功能更改为:
void updateButton(std::reference_wrapper<QAction*> currentButton)
{
...
}
并致电:
updateButton(std::ref(ui->actionDoAction));
这样做有任何好处,而不是QAction *¤tButton
?
简短答案
除非您希望修改原始指针本身,否则通过参考点传递而不是通过指针没有好处。
长答案
基于您的问题的构造方式以及对问题的评论的首次答复,您对提示和参考的实际误解具有根本性的误解。与您似乎相信的相反,它们不是对象本身。
让我们回到基本面。
为了存储数据,堆栈内存和堆内存。
,我们可以访问两个主要区域。当我们声明变量时,我们可以在堆栈内存中分配空间,并且可以使用变量名称访问数据。当变量从范围掉落时,内存会自动退出。
void myFunction() {
MyClass instance; // allocated in Stack memory
instance.doSomething();
} // instance gets automatically de-allocated here
最大的问题是,与普通程序需求相比,堆栈内存的数量极为有限,并且您经常需要数据以在某些范围之外持续存在,即在堆栈内存中创建大类实例通常是一个坏主意。那就是堆变得有用的地方。
不幸的是,有了堆内存,您需要接管内存分配的寿命。您也无法直接访问堆内存中的数据,您需要一种垫脚石才能到达那里。您必须明确要求操作系统进行内存分配,然后明确告诉它完成后将内存分配。C 为我们提供了两个操作员:new
和delete
。
void myFunction(){
MyClass *instance = new MyClass(); // ask OS for memory from heap
instance->doSomething();
delete instance; // tell OS that you don't need the heap memory anymore
}
您显然显然理解,在这种情况下,被称为pointer
。您似乎没有意识到的是,指针不是对象本身的实例,而是对象的"垫脚石"。指针的目的是持有该内存地址,以便我们不会丢失它,并使我们能够通过删除记忆位置来到达该内存。
在C 中,有两种方法可以做到这一点:您要么删除整个指针,然后像堆栈上的对象一样访问对象的成员;或者,您将使用成员使用运算符并使用它访问成员。
void myFunction(){
MyClass *instance = new MyClass();
(*instance).doSomething(); // older-style dereference-then-member-access
instance->doSomethingElse(); // newer-style using member dereference operator
}
指针本身只是整数的特殊构成。它们包含的值是分配对象的堆内存中的内存地址。它们的大小取决于您编制的平台(通常为32位或64位),因此将它们传递到周围的整数更昂贵。
我不能充分强调指针变量不是对象本身,而是将它们分配在堆栈内存中,并且在出现范围时的其他任何堆栈变量的行为。
void myFunction() {
MyClass *instance = new MyClass(); // pointer-sized integer of type 'pointer-to-MyClass' created in Stack memory
instance->doSomething();
} // instance is automatically de-allocated when going out of scope.
// Oops! We didn't explicitly de-allocate the object that 'instance' was pointing to
// so we've lost knowledge of it's memory location. It is still allocated in Heap
// memory but we have no idea where anymore so that memory is now 'leaked'
现在,因为下层的指针只不过是特殊用途的整数,而是将它们传递给任何其他类型的整数。
void myFunction(){
MyClass *instance = new MyClass(); // 'instance' is allocated on the Stack, and assigned memory location of new Heap allocation
instance->doSomething();
AnotherFunction(instance);
delete instance; // Heap memory pointed to is explicitly de-allocated
} // 'instance' is automatically de-allocated on Stack
void anotherFunction(MyClass *inst){ // 'inst' is a new pointer-to-MyClass on the Stack with a copy of the memory location passed in
inst->doSomethingElse();
} // 'inst' is automatically de-allocted
到目前为止,我尚未提及参考,因为它们与指针相同。它们也只是整数下的,但是通过使会员访问语法与堆栈变量相同来简化用法。与普通指针不同,必须使用有效的内存位置初始化参考文献,并且该位置无法更改。
以下在功能上等效:
MyClass &instance
MyClass * const instance
引用指针是双指导的,它们本质上是指针的指针,如果您想能够操纵不仅是堆对象,而且还包含包含内存位置到该堆对象的指针。<<<<<<<<<<<<<<<</p>
void myFunction(){
QString *str = new QString("First string"); // str is allocated in Stack memory and assigned the memory location to a new QString object allocated in Heap memory
substituteString(str);
delete str; // de-allocate the memory of QString("Second String"). 'str' now points to an invalid memory location
} // str is de-allocated automatically from Stack memory
void substituteString(QString *&externalString){ // 'externalString' is allocated in Stack memory and contains memory location of 'str' from MyFunction()
delete externalString; // de-allocate the Heap memory of QString("First string"). 'str' now points to an invalid location
externalString = new QString("Second string"); // Allocate new Heap memory for a new QString object. 'str' in MyFunction() now points to this new location
} // externalString is de-allocated automatically from Stack memory
如果我清楚地解释了自己,并且您已经跟随我了,现在您应该明白,在您的情况下,当您将QAction
的指针传递给函数时,您不会复制QAction
对象,而是您'重新将指针复制到该内存位置。由于指针仅仅是盖子下的整数,因此您只需复制32位或64位的东西(取决于您的项目设置),然后将其更改为参考点,绝对没有区别。
您的问题有几个不同的部分,我将分别解决每个部分。
通过指针的昂贵?
传递指针是基本上是免费的。如果要将其传递给的函数没有太多参数,则编译器将通过CPU寄存器中的指针传递,这意味着指针的值永远不必复制到堆栈中。
通过参考来传递的昂贵?
通过引用传递的东西有一些好处。
- 参考允许您假设参考点对一个有效对象(因此您不必检查参考的地址是否为null)。
- 当您通过引用时,您可以像常规对象一样使用它;无需使用
->
语法。 - 编译器可以(有时)生成更快的程序,因为它知道该参考无法重新分配。
但是,在引擎盖下的以与指针为相同的方式传递。两者都是地址。两者基本上都是免费的。
您应该使用哪一个?
- 如果您传递了一个大对象,需要修改,请将其作为const引用传递。
- 如果您传递了一个小物体,需要修改,请按值 将其传递
- 如果您传递了 do 需要修改的参考,请通过参考。
您正在修改什么?(按钮或按钮的指针?)
如果要修改按钮,请通过参考传递QACTION:
void pushButton(QAction& currentButton) {
currentButton.push();
}
如果要修改指针为"按钮" ,请通过参考将指针传递。这是一个不寻常的情况,我不明白您为什么要修改指针到按钮。
void modifyPointer(QAction*& buttonPointer) {
buttonPointer++; //I dunno, increment the pointer
}
更好,只需返回修改后的指针:
QAction* modifyPointer(QAction* buttonPointer) {
return buttonPointer + 1;
}
如果您将指针作为参数传递,几乎在所有情况下,您都需要按值传递它们。性能将是相同的,因为每个主要的C 编译器内部都将参考视为指示。
,或者您是说当您按值通过指针时会创建对象副本?事实并非如此 - 您可以根据需要将指针传递到最多,不会分配新的对象。
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- 数组的指针从不分段故障
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 何时在引用或唯一指针上使用移动语义
- QMetaObject invokeMethod的基于函数指针的语法
- 如何从 std::atomic 中提取指针 T<T>?
- 如何在 C# 中映射双 C 结构指针?
- C++将浮点指针值舍入为小数位数
- 为什么++(*p)更改指针值
- 调整大小后指向元素值的指针unordered_map有效?
- 正在将指针转换为范围
- 使用指向成员的指针将成员函数作为参数传递
- 将OpenCV C++重写为EmguCV C#-如何使用指针
- C++智能指针和指针到指针输出 API。模板化"wrapper"