C++ Primer阅读笔记--对象移动(右值引用、移动迭代器和引用限定符的使用)

目录

1--右值引用

2--std::move

3--移动构造函数

4--移动赋值运算符

5--移动迭代器

6--引用限定符


1--右值引用

右值引用必须绑定到右值的引用,通过 && 获得右值引用;

右值引用只能绑定到临时对象(即将被销毁的对象),即所引用的对象将要被销毁,对象没有其他用户;

返回非引用类型的函数,连同算术、关系、位已经后置递增/递减运算符,都生成右值;

cpp 复制代码
int i = 42;
int &r1 = i; // 左值引用,i是一个变量,是左值
int &&r2 = i * 42; // 右值引用,i*42是一个临时对象,是右值 

2--std::move

std::move 用于获得绑定到左值上的右值引用,其定义在头文件 utility 中;

cpp 复制代码
int &&r1 = 42; // 右值引用,但r1是一个左值
int &&r2 = std::move(r1); // 调用std::move,调用后只能对r1进行赋值或销毁,不能再使用

3--移动构造函数

移动构造函数的第一个参数是该类类型的一个右值引用

移动构造函数必须确保移动后,销毁源对象是无害的;

移动构造函数不分配任何新内存,只是接管给定的内存;

cpp 复制代码
A::A(A &&s) noexcept : data1(s.data1), data2(s.data2), data3(s.data3){
    s.data1 = s.data2 = s.data3 = nullptr;
}
// 假定data1,data2 和 data3 均是指针
// noexcept 的作用是通过标准库对于上述构造函数不抛出任何异常
// 在移动构造函数的函数体中,对源对象的指针数据进行赋值,可以避免由于源对象析构导致释放刚刚移动的内存的问题

4--移动赋值运算符

cpp 复制代码
A &A::operator=(A &&sample) noexcept{
    if(this != &sample){
        data1 = sample.data1;
        data2 = sample.data2;
        data3 = sample.data3;
        sample.data1 = sample.data2 = sample.data3 = nullptr;
    }
    return *this;
}

5--移动迭代器

移动迭代器的解引用运算符生成一个右值引用 ,通过调用标准库的 make_move_iterator 函数可以将一个普通迭代器转换为一个移动迭代器;

移动一个对象可能会销毁原对象,当确信一个算法为一个元素赋值或传递给函数后不会再访问原对象,才能使用移动迭代器将对象传递给算法;

cpp 复制代码
#include <iostream>     
#include <iterator>          
#include <string>       
#include <vector>

int main (int argc, char *argv[]){
   std::vector<std::string> foo (3);
   std::vector<std::string> bar {"A", "B", "C"};
   typedef std::vector<std::string>::iterator Iter;
   std::copy ( std::move_iterator<Iter>(bar.begin()), // 使用移动迭代器
               std::move_iterator<Iter>(bar.end()),
               foo.begin() );
   bar.clear(); // 移动 bar 后,清理
   std::cout << "foo:";
   for (std::string& x : foo) std::cout << ' ' << x;
   std::cout << std::endl;;
   return 0;
}

6--引用限定符

右值没有内存实体,一般不能对其进行调用成员函数或赋值,但有时会出现以下情况:即右值调用成员函数或对右值进行赋值;

cpp 复制代码
string s1 = "abc", s2 = "def";
auto n = (s1 + s2).find('a'); //(s1 + s2)是一个右值,对右值调用成员函数
s1 + s2 = "wc"; //(s1+s2)是一个右值,对右值赋值

上述代码其实是没意义的,但 C++11 仍然保留了这种右值赋值或调用成员函数的机制;通过使用引用限定符可以显式阻止函数被左值或右值调用:

cpp 复制代码
class demo{
	 int get_num();   // 默认情况下,成员函数既可以被左值或右值对象调用
	 int get_num()& ;  // &显式限制成员函数必须被左值成员对象调用
	 int get_num()&& ;  //&&显式限制成员函数必须被右值成员对象调用
}

class A{
	   A& operator=(const A&);
	   A& operator=(const A&) &;
	   A& operator=(const A&) &&;
}
相关推荐
史迪奇_xxx8 小时前
10、一个简易 vector:C++ 模板与 STL
java·开发语言·c++
我是华为OD~HR~栗栗呀10 小时前
华为od-21届考研-C++面经
java·c语言·c++·python·华为od·华为·面试
oioihoii10 小时前
C++ 中的类型转换:深入理解 static_cast 与 C风格转换的本质区别
java·c语言·c++
小妖66610 小时前
vscode 怎么运行 c++ 文件
开发语言·c++
lingran__10 小时前
算法沉淀第三天(统计二进制中1的个数 两个整数二进制位不同个数)
c++·算法
小冯记录编程11 小时前
深入解析C++ for循环原理
开发语言·c++·算法
磨十三12 小时前
C++ 容器详解:std::list 与 std::forward_list 深入解析
开发语言·c++·list
今麦郎xdu_12 小时前
【Linux系统】命令行参数和环境变量
linux·服务器·c语言·c++
情深不寿31714 小时前
C++特殊类的设计
开发语言·c++·单例模式
Vanranrr14 小时前
nullptr vs NULL:C/C++ 空指针的演变史
c语言·c++