汉中市网站建设设计制作一个生态瓶
汉中市网站建设,设计制作一个生态瓶,郴州网络推广公司在哪里,网站建设规划大纲阅读导航 引言一、左值引用和右值引用1. 什么是左值#xff1f;什么是左值引用#xff1f;2. 什么是右值#xff1f;什么是右值引用#xff1f;3. move( )函数 二、左值引用与右值引用比较三、右值引用使用场景和意义四、完美转发std::forward 函数完美转发实际中的使用场景… 阅读导航 引言一、左值引用和右值引用1. 什么是左值什么是左值引用2. 什么是右值什么是右值引用3. move( )函数 二、左值引用与右值引用比较三、右值引用使用场景和意义四、完美转发std::forward 函数完美转发实际中的使用场景 温馨提示 引言
当谈到C的高级特性时右值引用是一个不可忽视的重要概念。作为一种在C11标准中引入的语言特性右值引用为我们提供了更加灵活和高效的内存管理方式。它不仅可以优化代码性能还可以改善对象拷贝行为使得我们能够更好地处理临时对象和移动语义。通过深入理解右值引用的原理和使用方法我们可以在C编程中发挥出更大的威力提升代码的效率和可维护性。本文将全面介绍右值引用的概念、用法和相关的重要概念帮助读者更好地理解和应用这一关键特性。无论您是初学者还是有经验的程序员都将从本文中获得对右值引用的深入认识并能够在实际项目中灵活运用。让我们一起探索C中右值引用的奇妙世界吧
一、左值引用和右值引用
传统的C语法中就有引用的语法而C11中新增了的右值引用语法特性总的来说就是无论左值引用还是右值引用都是给对象取别名。
1. 什么是左值什么是左值引用
在C中左值是指表达式结束后依然存在的数据对象它可以出现在赋值操作的左边或右边。通常来说变量、函数返回的引用、解引用操作等都是左值。简言之左值可以被赋值可以取地址。
左值引用是指对左值进行引用的方式。它使用符号声明可以绑定到一个左值上从而允许我们通过引用修改原始的左值对象。左值引用就是给左值的引用给左值取别名。左值引用在函数参数传递和函数返回值中经常被使用能够避免不必要的复制并且可以实现对原始对象的直接操作。左值引用也为后续引入右值引用打下了基础是C语言中非常重要的概念之一。
int main()
{// 以下的p、b、c、*p都是左值int* p new int(0);int b 1;const int c 2;// 以下几个是对上面左值的左值引用int* rp p;int rb b;const int rc c;int pvalue *p;return 0;
}2. 什么是右值什么是右值引用
在C中右值是指表达式结束后即将被销毁的临时数据对象它通常不能出现在赋值操作的左边。字面上来说右值就是“赋值运算符右边的值”。比如常量、临时对象、表达式的计算结果等都可以是右值。
右值引用是C11引入的新特性使用双符号声明它可以绑定到一个右值或将要销毁的对象上。右值引用的引入使得我们能够实现移动语义即将资源如内存的所有权从一个对象转移到另一个对象而不需要进行深层的复制操作从而提高了代码的效率和性能。右值引用还为移动构造函数和移动赋值运算符的实现提供了基础这些特性在处理大型数据结构时非常有用。右值引用的引入使得C语言能够更好地支持移动语义从而更好地适应现代编程的需求。
int main()
{double x 1.1, y 2.2;// 以下几个都是常见的右值10;x y;fmin(x, y);// 以下几个都是对右值的右值引用int rr1 10;double rr2 x y;double rr3 fmin(x, y);// 这里编译会报错error C2106: “”: 左操作数必须为左值10 1;x y 1;fmin(x, y) 1;return 0;
}需要注意的是右值是不能取地址的但是给右值取别名后会导致右值被存储到特定位置且可以取到该位置的地址也就是说例如不能取字面量10的地址但是rr1引用后可以对rr1取地址也可以修改rr1。如果不想rr1被修改可以用const int rr1 去引用是不是感觉很神奇这个了解一下实际中右值引用的使用场景并不在于此这个特性也不重要。
int main()
{double x 1.1, y 2.2;int rr1 10;const double rr2 x y;rr1 20;rr2 5.5; // 报错return 0;
}3. move( )函数
std::move() 是C11引入的一个函数模板位于头文件 utility 中。它用于将传入的对象转换为右值引用从而支持移动语义。在移动语义中对象的资源所有权可以从一个对象转移到另一个对象而不需要进行深层的复制操作这可以提高程序的性能和效率。
std::move() 的定义如下
template class T
constexpr remove_reference_tT move(T t) noexcept;其中t 是一个通用引用它可以绑定到左值或右值。std::move() 将 t 转换为右值引用并返回即使 t 是一个左值也可以通过 std::move() 转为右值引用。
使用 std::move() 主要用于以下两个场景
在实现移动构造函数和移动赋值运算符时可以使用 std::move() 将成员变量转换为右值引用从而实现资源的转移而非复制。在标准库中例如容器的 insert 和 emplace 方法中使用 std::move() 可以将对象的所有权转移到容器中避免不必要的复制操作。
需要注意的是std::move() 仅仅是将对象转换为右值引用它本身并不会进行实际的资源移动操作。因此在使用 std::move() 后程序员仍需谨慎处理对象的生命周期以避免悬挂指针或对象被多次释放等问题。
二、左值引用与右值引用比较
⭕左值引用总结
左值引用只能引用左值不能引用右值。但是const左值引用既可引用左值也可引用右值
int main()
{// 左值引用只能引用左值不能引用右值。int a 10;int ra1 a; // ra为a的别名//int ra2 10; // 编译失败因为10是右值// const左值引用既可引用左值也可引用右值。const int ra3 10;const int ra4 a;return 0;
}⭕右值引用总结
右值引用只能右值不能引用左值。但是右值引用可以move以后的左值.
int main()
{// 右值引用只能右值不能引用左值。int r1 10;// error C2440: “初始化”: 无法从“int”转换为“int ”// message : 无法将左值绑定到右值引用int a 10;int r2 a;// 右值引用可以引用move以后的左值int r3 std::move(a);return 0;
}三、右值引用使用场景和意义
移动语义右值引用的最重要的使用场景之一就是实现移动语义。通过移动语义可以避免不必要的深层复制操作提高程序的性能和效率。移动语义通常在以下情况下使用 移动构造函数和移动赋值运算符通过将资源的所有权从一个对象转移到另一个对象而非进行深层的复制操作来提高效率。标准库中的容器和算法许多标准库中的容器和算法都利用了移动语义例如移动构造和移动赋值来提高性能。
例如在下面这段代码中使用了右值引用来实现移动语义从而避免不必要的深层复制操作提高了对象的构造和赋值效率。
移动构造函数的定义如下
string::string(string s): _str(nullptr), _size(0), _capacity(0)
{cout string(string s) -- 移动语义 endl;swap(s);
}在移动构造函数中接收一个右值引用作为参数通过 标识符表示。在函数体内部输出一条信息以表明这是移动构造函数并且调用了 swap() 函数来交换资源实现了移动语义。
移动赋值运算符的定义如下
string string::operator(string s)
{cout string operator(string s) -- 移动语义 endl;swap(s);return *this;
}在移动赋值运算符中同样接收一个右值引用作为参数。在函数体内部输出一条信息以表明这是移动赋值运算符并且调用了 swap() 函数来交换资源实现了移动语义。 完美转发右值引用与通用引用universal reference结合使用时可以实现完美转发。完美转发允许将函数参数按原样传递给其他函数无论原始参数是左值还是右值。这对于泛型编程以及实现转发函数forwarding function非常有用。 优化临时对象临时对象是在表达式求值过程中创建的临时值它们的生命周期很短暂并且通常在表达式结束后立即销毁。通过使用右值引用可以避免不必要的拷贝构造和析构操作提高代码的性能和效率。 移动语义和资源管理右值引用在资源管理方面非常有用例如管理动态分配的内存、文件句柄、网络连接等。通过使用右值引用可以实现资源的移动而非复制从而提高程序的性能和可维护性。 避免不必要的拷贝构造和析构当需要返回临时对象时通过使用右值引用可以避免不必要的拷贝构造和析构提高代码的效率。
四、完美转发
⭕模板中的 万能引用
void Fun(int x){ cout 左值引用 endl; }
void Fun(const int x){ cout const 左值引用 endl; }
void Fun(int x){ cout 右值引用 endl; }
void Fun(const int x){ cout const 右值引用 endl; }模板中的不代表右值引用而是万能引用其既能接收左值又能接收右值。模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力但是引用类型的唯一作用就是限制了接收的类型后续使用中都退化成了左值我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
std::forward 函数
std::forward 是C标准库中的一个函数模板位于 utility 头文件中。它用于实现完美转发将传入的参数以原样转发给其他函数。
std::forward 的函数模板定义如下
template typename T
T forward(typename std::remove_referenceT::type arg) noexcept;template typename T
T forward(typename std::remove_referenceT::type arg) noexcept;这个函数模板有两个重载版本接受一个通用引用作为参数。它使用了 typename std::remove_referenceT::type 来移除参数的引用限定符以保持参数的值类别左值或右值。
当传入一个左值时std::forward 返回一个左值引用当传入一个右值时std::forward 返回一个右值引用。这样就可以保持参数在转发过程中的值类别不变。
std::forward 的主要应用场景是在模板函数中进行完美转发将参数原样传递给其他函数。通过使用 std::forward可以避免不必要的拷贝和移动操作提高代码的性能和效率。
以下是使用 std::forward 进行完美转发的示例
void Fun(int x){ cout 左值引用 endl; }
void Fun(const int x){ cout const 左值引用 endl; }void Fun(int x){ cout 右值引用 endl; }
void Fun(const int x){ cout const 右值引用 endl; }// std::forwardT(t)在传参的过程中保持了t的原生类型属性。
templatetypename Tvoid PerfectForward(T t)
{Fun(std::forwardT(t));
}int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}完美转发实际中的使用场景
templateclass T
struct ListNode
{ListNode* _next nullptr;ListNode* _prev nullptr;T _data;
};templateclass T
class List
{typedef ListNodeT Node;public:List(){// 创建一个头节点并将头节点的_next和_prev都指向自身表示链表为空_head new Node;_head-_next _head;_head-_prev _head;}void PushBack(T x){// 在链表尾部插入一个右值Insert(_head, std::forwardT(x));}void PushFront(T x){// 在链表头部插入一个右值Insert(_head-_next, std::forwardT(x));}void Insert(Node* pos, T x){// 在指定位置之前插入一个右值// 获取pos节点的前一个节点Node* prev pos-_prev;// 创建一个新的节点Node* newnode new Node;// 使用完美转发将右值x赋值给新节点的_datanewnode-_data std::forwardT(x);// 调整链表中的指针prev-_next newnode;newnode-_prev prev;newnode-_next pos;pos-_prev newnode;}void Insert(Node* pos, const T x){// 在指定位置之前插入一个左值// 获取pos节点的前一个节点Node* prev pos-_prev;// 创建一个新的节点Node* newnode new Node;// 将左值x赋值给新节点的_datanewnode-_data x;// 调整链表中的指针prev-_next newnode;newnode-_prev prev;newnode-_next pos;pos-_prev newnode;}private:Node* _head;
};int main()
{Listbit::string lt;lt.PushBack(1111);lt.PushFront(2222);return 0;
}
上面这段代码是一个简化的链表实现包括了节点结构 ListNode 和链表类 List。其中链表类中的 PushBack、PushFront 和 Insert 函数用于在链表中插入元素。
在 Insert 函数中有两个重载版本分别用于插入右值引用和左值引用。关键位置是对节点的 _data 成员赋值的地方。
对于右值引用版本使用 std::forwardT(x) 将参数 x 原样转发保持其原始值类别。这样做可以避免不必要的拷贝操作提高性能和效率。
对于左值引用版本直接将参数 x 赋值给节点的 _data 成员。因为左值引用已经是一个具名对象没有必要进行移动或拷贝操作。
在主函数中创建了一个 Listbit::string 类型的链表对象 lt并通过 PushBack 和 PushFront 函数向链表中插入元素。
总的来说这段代码展示了如何使用完美转发和模板来实现一个简单的链表并在插入元素时考虑了右值引用和左值引用的情况以提高代码的灵活性和效率。
温馨提示
感谢您对博主文章的关注与支持另外我计划在未来的更新中持续探讨与本文相关的内容会为您带来更多关于C以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新不要错过任何精彩内容
再次感谢您的支持和关注。期待与您建立更紧密的互动共同探索C、算法和编程的奥秘。祝您生活愉快排便顺畅
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/88790.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!