C++11移动语义解析

本文详细阐述了C++11中移动语义的应用场景,包括移动构造函数和移动赋值运算符的触发条件,以及如何通过std::move避免对象拷贝。通过实例解析了不同情况下临时对象的处理,如函数返回值、参数传递和赋值操作。
摘要由CSDN通过智能技术生成

当给函数传递对象当做函数参数时,可以使用引用类型来减少拷贝对象的代价,尤其是避免容器的拷贝等。 但是当把函数内的局部对象当做返回值时,我们无法返回该局部对象的引用,导致每次返回局部对象都会进行拷贝。 因为返回局部对象的引用是无意义的,当函数调用完成,局部对象就被析构,所以其引用指向了一块析构的内存。程序如果使用移动操作,避免了拷贝,将新变量指向了局部变量的内容。例如:

std::vector<int> GetNums() {
  std::vector<int> nums;
  nums.push_back(1);
  nums.push_back(2);
  return nums;
}


std::vector<int> result = GetNums();  // 调用vector的移动构造函数,避免了拷贝。
标准库的容器vector,string等实现了拷贝语义,所以这些容器作为函数的局部对象时都可以直接返回。
 

C++11提供了移动语义,分别是移动构造函数和移动赋值运算符,那么什么时候会触发移动语义?

当右值引用被当做初始值或者赋值操作的右侧运算对象时,程序将使用移动操作。

1. 对于移动构造函数两种情况会触发移动语义:函数返回把临时对象当做返回值,使用std::move。

2. 对于移动赋值运算符三种情况会触发移动语义:函数返回把临时对象当做返回值,使用std::move, 临时对象。

以以下代码为例:


#include <string.h> 
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <utility>
#include <functional>
#include <algorithm>
#include <cassert>
 
class Student {
public:
  Student() {
    id_ = 0;
    name_ = nullptr;
  }
 
  Student(int id, const char* name) {
    std::cout << "Constructor" << std::endl;
    id_ = id;
    size_t len = strlen(name);
    name_ = new char[len + 1];
    strcpy(name_, name);
  }
 

  ~Student() {
    std::cout << "De-constructor" << std::endl;
    id_ = 0;
    if (name_ != nullptr) {
      delete[] name_;
      name_ = nullptr;
    }
  }
 
  Student(const Student& student) {
    std::cout << "Copy Constructor" << std::endl;
 
    id_ = student.id_;
 

    size_t len = strlen(student.name_);
    name_ = new char[len + 1];
    strcpy(name_, student.name_);
  }
 
  Student(Student&& student) {
    std::cout << "Move Constructor" << std::endl;
     id_ = student.id_;
     name_ = student.name_;
 
    student.id_ = 0;
    student.name_ = nullptr;
  }
 
  Student& operator=(const Student& student) {
    std::cout << "Copy Assignment" << std::endl;
 
    if (this == &student) {
      return *this;
    }
 
    id_ = student.id_;
 
    size_t len = strlen(student.name_);
    name_ = new char[len + 1];
    strcpy(name_, student.name_);
 
    return *this;
  }
 
  Student& operator=(Student&& student) {
    std::cout << "Move Assignment" << std::endl;
 
    if (this == &student) {
      return *this;
    }
    if (name_ != nullptr) {
      delete[] name_;
    }
 
    id_ = student.id_;
    name_ = student.name_;
 
    student.id_ = 0;
    student.name_ = nullptr;
 
    return *this;
  }
 
  void Print() {
    std::cout << id_ << " : " << (name_ == nullptr ? "NULL": name_) << std::endl;
  }
 
private:
  int id_;
  char* name_;
};
 
void PrintStudent(Student student) {
  student.Print();
}
 
Student GetStudent() {
  printf("get student\r\n");
  Student student(123, "123");
  return student;
}
 
int main() {
  Student a(1, "1");
  std::cout << "====================================" << std::endl;
 
  PrintStudent(a);  
  //copy constructor : 按值传递形参,需要将a拷贝到一个临时对象‘tempA’
  //de-con :tempA析构
  std::cout << "================1====================" << std::endl;
 
  PrintStudent(Student(2, "2"));  
  //con:(2,'2')构建一个临时对象作为形参传递给PrintStudent(引用方式)
  //de-con:临时对象析构
  std::cout << "================2====================" << std::endl;
 
  PrintStudent(std::move(Student(2, "2")));  
  //con: (2,"2")构建一个临时对象A(左值)
  //move con:将临时对象A move成右值引用对象B
  //de-con: 临时对象A析构
  //de-con:临时对象B析构
  std::cout << "================3====================" << std::endl;
 
  PrintStudent(std::move(a));  
  a.Print();  
  //move con:将对象a move成右值引用对象b
  //de-con: 对象b析构
  std::cout << "================4====================" << std::endl;
 
  Student a2 = GetStudent(); 
  //con:当函数返回值为对象时,返回临时对象的引用,相当于直接构造新对象避免了拷贝
  std::cout << "================5====================" << std::endl;
 
  Student a3;
  a3 = Student(3, "3");  
  //con:构造临时对象
  //move assign:将临时对象copy给a3, 此时用的是move assign
  //de-con: 临时对象析构
  std::cout << "================6====================" << std::endl;
 
  Student a4(4, "4");
  a3 = a4;  
  //con:构造对象a4
  //copy assign: 对象a4赋值给a3, 此时用的是copy assign
  std::cout << "================7====================" << std::endl;
 
  a3 = std::move(a4);  
  //move assign : 将对象a4 move成右值引用对象3,此时用的是move assign
  std::cout << "================8====================" << std::endl;
 
  a3 = GetStudent();  

  //con : 构造对象student
  //move assign: student对象赋值给a3, 此时用的是move assign
  //de-con: student对象析构
  std::cout << "================9====================" << std::endl;
 
  return 0;
}

参考文章:

C++11右值引用(一看即懂)​​​​​​c++ 之 std::move 原理实现与用法总结_学之知之的博客-CSDN博客_std::move

C++11的移动语义_博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条叫做nemo的鱼

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值