c++11以上的移动语义

本文介绍了C++中的左值和右值概念,以及新标准中引入的glvalue、prvalue和xvalue。移动语义作为提高效率的重要特性,通过std::move等方法避免了对象复制,减少了资源开销。示例展示了移动语义在不同类型对象中的应用,并探讨了在不同场景下如何有效利用移动语义。最后,强调了在实践中学习和使用新标准的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、左值和右值

在前面谈过左值和右值,这里不再谈。需要注意的是左值有引用,那么新的c++标准中,右值也是有引用的。在c++新标准中,细分了lvalue,rvalue,glvalue(泛左值),prvalue(纯右值),xvalue(消亡值)。这些详细的说明可以参看一下c++11以上的标准文档。一般人都是以等号左右来区分左右值,其实这是不完全准确的。其实无法取地址的即为右值。不要迷惑的是,右值引用也是左值。

二、移动语义

移动语义和右值是密不可分的,这也是c++不断的引入右值来处理一些问题的原因。大家都知道在std::vector向量的插入操作过程中,需要不断的进行临时变量的复制和销毁,这对于c++编程人员来说,是不可忍受的(当然,指的是有些人)。能不能够做到这些临时对象的变量不再复制而是直接使用。这样既减少了内存复制的开销,又减小了释放对象的过程,一举多得,再好不过。
解决问题的方法有两类,一种是传统的使用输出参数,也就是说不使用返回值而是直接通过参数来传递结果;另外一种就是移动语义。
所谓移动语义,其实就是上面提到的把对象转移到指定的目标,而不是通过复制。它分为显示和隐式两种。显示的移动一个对象可以使用std::move这个函数来实现;而隐式的可以使用移动构造函数、移动赋值操作符等来实现。移动语义的出现,可以说对c++的操作运算有一个补全和提高效率的作用,而这也恰恰是c++的优势,也就是说,在优势上,更加具有了优势。
std::unique_ptr是c++11中的一个只能移动不能复制的类型,如果写过条件变量的锁操作的程序员可能印象会比较深刻。移动后的对象,原操作就不能再使用了,否则可能会引起不可知的后果(可能是崩溃)。一般在编写移动成员时,建议增加noexcept关键字,这个很重要。
在c++的传统面试题中,有一个拷贝构造函数的编写,其中使用“复制交换”可以提高效率,那么同样也可以使用“移动交换”,既可以提高效率又可以减少开销。

三、实例

说多少不如看一个例子更明白:

#include <string>
#include <iostream>
#include <utility>
 
struct A
{
    std::string s;
    A() : s("test") { }
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
    A(A&& o) : s(std::move(o.s)) { }
    A& operator=(const A& other)
    {
         s = other.s;
         std::cout << "copy assigned\n";
         return *this;
    }
    A& operator=(A&& other)
    {
         s = std::move(other.s);
         std::cout << "move assigned\n";
         return *this;
    }
};
 
A f(A a) { return a; }
 
struct B : A
{
     std::string s2; 
     int n;
     // implicit move assignment operator B& B::operator=(B&&)
     // calls A's move assignment operator
     // calls s2's move assignment operator
     // and makes a bitwise copy of n
};
 
struct C : B
{
    ~C() { } // destructor prevents implicit move assignment
};
 
struct D : B
{
    D() { }
    ~D() { } // destructor would prevent implicit move assignment
    D& operator=(D&&) = default; // force a move assignment anyway 
};
 
int main()
{
    A a1, a2;
    std::cout << "Trying to move-assign A from rvalue temporary\n";
    a1 = f(A()); // move-assignment from rvalue temporary
    std::cout << "Trying to move-assign A from xvalue\n";
    a2 = std::move(a1); // move-assignment from xvalue
 
    std::cout << "Trying to move-assign B\n";
    B b1, b2;
    std::cout << "Before move, b1.s = \"" << b1.s << "\"\n";
    b2 = std::move(b1); // calls implicit move assignment
    std::cout << "After move, b1.s = \"" << b1.s << "\"\n";
 
    std::cout << "Trying to move-assign C\n";
    C c1, c2;
    c2 = std::move(c1); // calls the copy assignment operator
 
    std::cout << "Trying to move-assign D\n";
    D d1, d2;
    d2 = std::move(d1);
}

这个例子还是比较全面的。看一下那个移动交换的:

template <typename T> 
Array<T>&Array<T>::operator=(Array && rhs) noexcept
{
  Array<T> moved(std::move(rhs));
  swap(moved);
  return *this;
}
  • 这个例子出自《c++17入门经典》,里面有一个对比的例子,有兴趣可以下载看一看,很有说明力。
    大家可以好好的和复制交换比一比。

四、总结

vistual studio2022中,对c++20的支持很好(默认是c++14,下面有c++17、c++20和更新的c++23等),所以如果对Linux命令行有抵触的一些程序员,接触新的c++标准,建议使用这个。但对广大的老鸟儿们来说,在Linux上搞更新的c++才能体现自己的狂野的一面,“工欲善其事,必先利其器”,手段不同而已,不要过多纠结。
对于新标准,要勤学多用,特别在工程实践上,用得多了,自然就熟悉了,很自然就融于自己的思想过程,正所谓信手拈来,无一而不至其极!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值