C++赋值函数+移动赋值函数+移动构造函数详解

目录
  • 左值引用和右值引用
    • 左值与右值
    • 左右值的切换
  • 左值引用:将左值绑定在引用上
    • 常量左值引用和非常量左值引用
  • 右值引用:将右值绑定在引用上
    • 常量右值引用和非常量右值引用
  • 移动构造函数
  • 赋值和移动赋值函数

左值引用和右值引用

左值与右值

左值:在内存中占有确定位置的对象,即左值占有内存。换句话来说,就是有地址,有值。

右值:不占内存(科学地讲是临时寄存器),仅有值,为临时变量。

左右值的切换

右值->左值:用*符号。

int a=10;
int* b=&a;// b为右值。
*b=20;// b为右值,*b为左值。

左值->右值:用&符号。

int a = 10;
&a = 40; //错误:赋值操作要求一个左值
int* b = &(a + 1); //错误:‘&'运算符要求一个左值,a为左值,但a+1为右值。
int* c = &a; //正确:var是左值

左值引用:将左值绑定在引用上

第一种情况,函数返回右值。

int global = 10;
int test()
{
return global;// 返回右值
}
int main()
{
test() = 20;// error,右值不可赋值!
cout << "test为:"<<test();
return 0;
}

第二种情况,函数返回左值。

int global = 10;
int& test()
{
return global;// 返回左值
}
int main()
{
test() = 20;// 左值可赋值
cout << "test为:"<<test();
return 0;
}

测试:

说明了左值引用让函数调用可以赋值成为可能。

常量左值引用和非常量左值引用

int a1=20; //非常量左值
const int a2=20; //常量左值
const int a3=20; //常量左值

//非常量左值引用
int &b1=a1; //正确,a1是一个非常量左值,可以被非常量左值引用绑定
int &b2=a2; //错误,a2是一个常量左值,不可以被非常量左值引用绑定
int &b3=20; //错误,10是一个非常量右值,不可以被非常量左值引用绑定
int &b4=a2+a3; //错误,(a2+a3)是一个常量右值,不可以被非常量左值引用绑定

//常量左值引用
const int &c1=a1; //正确,a1是一个非常量左值,可以被非常量右值引用绑定
const int &c2=a2; //正确,a2是一个常量左值,可以被非常量右值引用绑定
const int &c3=a1+a2; //正确,(a1+a2)是一个非常量右值,可以被常量右值引用绑定
const int &c4=a2+a3; //正确,(a2+a3)是一个常量右值,可以被非常量右值引用绑定

总结:

  • 1.非常量左值引用只能绑定到非常量左值上;
  • 2.常量左值引用可以绑定到非常量左值、常量左值、非常量右值、常量右值等所有类型。

(大->小,小引用绑大左值,常量左值范围更小嘛)

右值引用:将右值绑定在引用上

常量右值引用和非常量右值引用

总结:

  • 1.非常量右值引用只能绑定到非常量右值上;
  • 2.常量右值引用可以绑定到非常量右值、常量右值上。

移动构造函数

小狗狗类:

Dog(int age,string name) :m_age(new int(age)), m_name(name){}
Dog(Dog& d):m_age(d.m_age),m_name(d.m_name)
{
cout << "我是拷贝构造函数······" << endl;
}
Dog(Dog&& d) :m_age(d.m_age), m_name(d.m_name)
{
d.m_age = nullptr;
cout << "我是移动构造函数······" << endl;
}
int* m_age;
string m_name;#include<string>
#include<iostream>
using namespace std;
class Dog
{
public:
Dog(){};
Dog(int age,string name) :m_age(new int(age)), m_name(name){}
Dog(Dog& d):m_age(d.m_age),m_name(d.m_name)
{
cout << "我是拷贝构造函数······" << endl;
}
Dog(Dog&& d) :m_age(d.m_age), m_name(d.m_name)
{
d.m_age = nullptr;
cout << "我是移动构造函数······" << endl;
}
int* m_age;
string m_name;
};

客户端类:

#include"construct.h"
int main()
{
int age = 19;
string name = "小狗狗";
Dog d1(age, name);
cout << "d1:" <<* d1.m_age << d1.m_name << endl;
Dog d2(move(d1));
bool is = d1.m_age == nullptr;
cout << is << endl;
cout << "d2.age:" << *d2.m_age <<endl<< "d2.name:" <<d2.m_name << endl;
return 0;
}

测试:

说明了移动构造运行成功,d1销毁了,d2获取到了d1的内存。

赋值和移动赋值函数

赋值函数类:

#include<String>
using namespace std;
class Cat
{
public:
Cat(){}
Cat(int age ,string name):age(age),name(name) {}
Cat& operator=(Cat& c)
{
if (this!= &c)
{
age = c.age;
name = c.name;
}
return *this;
}
int age;
string name;
};

移动赋值函数类:

#include"assign.h"
class Dog
{
public:
Dog() {}
Dog(int age, string name) :age(new int(age)), name(name) {}

Dog& operator=(Dog&& c)
{
age = c.age;
name = c.name;
c.age = nullptr;
return *this;
}
int* age;
string name;
};

客户端类:

#include<iostream>
#include"moveAssign.h"
int main()
{
cout << "赋值函数" << endl;
Cat c1(18, "小猫咪");
Cat c2 ;
c2 = c1;
cout << "c1.age:" << c1.age << endl << "c1.name" << c1.name << endl;
cout << "c2.age:" << c2.age << endl << "c2.name" << c2.name << endl<<endl;;
cout << "移动赋值函数" << endl;
Dog d1(19,"小狗狗");
cout << "d1.age:" << *d1.age << endl << "d1.name" << d1.name << endl << endl;
Dog d2;
d2 = move(d1);
bool is = (d1.age == nullptr);
cout << "d1是否为空:" << is << endl;
cout << "d2.age:" << *d2.age << endl << "d2.name" << d2.name << endl;
return 0;
}

测试:

到此这篇关于C++赋值函数+移动赋值函数+移动构造函数详解的文章就介绍到这了,更多相关C++赋值函数 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C++中对构造函数和赋值运算符的复制和移动操作

    复制构造函数和复制赋值运算符 从 C++ 11 中开始,该语言支持两种类型的分配:复制赋值和移动赋值. 在本文中,"赋值"意味着复制赋值,除非有其他显式声明. 赋值操作和初始化操作都会导致对象被复制. 赋值:在将一个对象的值赋给另一个对象时,第一个对象将复制到第二个对象中. 因此, Point a, b; ... a = b; 导致 b 的值被复制到 a 中. 初始化:在以下情况下将进行初始化:声明新对象.参数通过值传递给函数或值通过值从函数返回. 您可以为类类型的对象定义"

  • 聊聊C++中右值引用和移动构造函数的使用

    目录 一: 背景 二: 右值引用 1. 它到底解决了什么问题 2. 右值引用是个什么样子 三: 右值引用如何减少对象的创建 1. 简要思路 2. 一个简单的例子 3. 性能优化方案 四: 总结 一: 背景 最近在看 C++ 的右值引用和移动构造函数,感觉这东西一时半会还挺难理解的,可能是没踩过这方面的坑,所以没有那么大的深有体会,不管怎么说,这一篇我试着聊一下. 二: 右值引用 1. 它到底解决了什么问题 在其他编程语言中,很少听到 右值引用 这个词,我个人感觉还是 C++ 这个 值类型 优先的

  • C++超详细讲解拷贝构造函数

    目录 构造函数 特征 编译器生成的拷贝构造 拷贝构造的初始化列表 显式定义拷贝构造的误区 结论 构造函数 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用 拷贝构造函数是构造函数的一个重载,因此显式的定义了拷贝构造,那么编译器也不再默认生成构造函数. 特征 拷贝构造也是一个特殊的成员函数 特征如下: 拷贝构造是构造函数的一个重载: 拷贝构造的参数只有一个并且类型必须是该类的引用,而不是使用传值调用,否则会无限递归: 若没有显

  • C++11中std::move、std::forward、左右值引用、移动构造函数的测试问题

    关于C++11新特性之std::move.std::forward.左右值引用网上资料已经很多了,我主要针对测试性能做一个测试,梳理一下这些逻辑,首先,左值比较熟悉,右值就是临时变量,意味着使用一次就不会再被使用了.针对这两种值引入了左值引用和右值引用,以及引用折叠的概念. 1.右值引用的举例测试 #include <iostream> using namespace std; ​ //创建一个测试类 class A { public: A() : m_a(55) { } ​ int m_a;

  • C++中SetConsoleCursorPosition()移动光标函数的用法大全

    SetConsoleCursorPosition()来自于文件"windows.h",使用时记得引用此头文件. 首先说一下,这个函数的功能即是字面意思,即移动命令行中光标的位置.这里要注意的是,每次调用这个函数都是默认从左上角开始偏移,而与当前光标停留的位置无关. 然后我们剖析下这个函数,我们查看定义发现,调用这个函数需要传入两个参数,都是自定义类型,分别为 HANDLE 和 COORD. SetConsoleCursorPosition( _In_ HANDLE hConsoleOu

  • C++中的移动构造函数及move语句示例详解

    前言 本文主要给大家介绍了关于C++中移动构造函数及move语句的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看一个小例子: #include <iostream> #include <cstring> #include <cstdlib> #include <vector> using namespace std; int main() { string st = "I love xing"; vec

  • C++超详细讲解构造函数

    目录 类的6个默认成员函数 构造函数 特性 编译器生成的默认构造函数 成员变量的命名风格 类的6个默认成员函数 如果我们写了一个类,这个类我们只写了成员变量没有定义成员函数,那么这个类中就没有函数了吗?并不是的,在我们定义类时即使我们没有写任何成员函数,编译器会自动生成下面6个默认成员函数. class S { public: int _a; }; 这里就来详细介绍一下构造函数. 构造函数 使用C语言,我们用结构体创建一个变量时,变量的内容都是随机值,要想要能正确的操作变量中存储的数据,我们还需

  • C++11 移动构造函数的使用

    目录 一.引言 二.左值和右值 三.深拷贝构造函数 四.右值引用 五.移动构造函数 六.std::move() 七.参考资料 一.引言 移动构造函数是什么?先举个例子,你有一本书,你不想看,但我很想看,那么我有哪些方法可以让我能看这本书?有两种做法,一种是你直接把书交给我,另一种是我去买一些稿纸来,然后照着你这本书一字一句抄到稿纸上. 显然,第二种方法很浪费时间,但这正是有些深拷贝构造函数的做法,而移动构造函数便能像第一种做法一样省时,第一种做法在 C++ 中叫做完美转发. 二.左值和右值 何为

  • C++赋值函数+移动赋值函数+移动构造函数详解

    目录 左值引用和右值引用 左值与右值 左右值的切换 左值引用:将左值绑定在引用上 常量左值引用和非常量左值引用 右值引用:将右值绑定在引用上 常量右值引用和非常量右值引用 移动构造函数 赋值和移动赋值函数 左值引用和右值引用 左值与右值 左值:在内存中占有确定位置的对象,即左值占有内存.换句话来说,就是有地址,有值. 右值:不占内存(科学地讲是临时寄存器),仅有值,为临时变量. 左右值的切换 右值->左值:用*符号. int a=10; int* b=&a;// b为右值. *b=20;//

  • javascript设计模式之对象工厂函数与构造函数详解

    下面通过文字详解加代码分析的方式给大家分享下javascript设计模式之对象工厂函数与构造函数的相关知识. 概述使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法.然而,除了这两种常用的对象创建方式,JavaScript还提供了其他方法创建对象.1).使用工厂函数创建对象我们可以编写一个函数,此函数的功能就是创建对象,可将其. 概述 使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法. 然而,除了这两种常用的对象创建方式,JavaScript还提

  • C++继承的赋值转换与菱形虚拟继承深入详解

    目录 一.继承的概念及定义 1.1.继承的概念 1.2.继承的定义 二.基类和派生类对象赋值转换 三.继承中的作用域 3.1.继承同名成员处理方式 3.2.继承同名静态成员处理方式 3.3.继承与友元 3.4.继承与静态成员 四.派生类的默认成员函数 五.复杂菱形继承及菱形虚拟继承 5.1.继承分类 5.2.虚拟继承解决菱形继承问题原理 一.继承的概念及定义 继承是面向对象三大特性之一. 1.1.继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许

  • 基于Python函数的作用域规则和闭包(详解)

    作用域规则 命名空间是从名称到对象的映射,Python中主要是通过字典实现的,主要有以下几个命名空间: 内置命名空间,包含一些内置函数和内置异常的名称,在Python解释器启动时创建,一直保存到解释器退出.内置命名实际上存在于一个叫__builtins__的模块中,可以通过globals()['__builtins__'].__dict__查看其中的内置函数和内置异常. 全局命名空间,在读入函数所在的模块时创建,通常情况下,模块命名空间也会一直保存到解释器退出.可以通过内置函数globals()

  • Python内置函数zip map filter的使用详解

    并行遍历zip zip会取得一个或多个序理为参数,然后返回元组的列表,将这些序列中的并排的元素配成对. L1=[1,2,3,4] L2=[5,6,7,8] L3=zip(L1,L2) print(L3,type(L3)) <zip object at 0x7feb81b17f08> <class 'zip'> zip在python3中是一个可迭代对象,我们可以将其包含在list调用中以例一次性显示所有结果 list(L3) [(1, 5), (2, 6), (3, 7), (4,

  • JS函数(普通函数,箭头函数)中this的指向问题详解

    目录 普通函数 箭头函数 普通函数 具名普通函数.匿名普通函数,在不作为对象的属性值的情况下,其内部的 this 总是指向代码运行环境下的全局对象 ( 例如,浏览器中的 window ). 示例: (function() { console.log(this); // window (function() { console.log(this); // window (function() { console.log(this); // window })() })() })() 普通函数,均可

  • python中函数总结之装饰器闭包详解

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器就是包装原来的函数,从而在不需要修改原来代码的基础之上,可以做更多的事情. 装饰器语法如下: @deco2 @deco1 def func(arg1,arg2...): pass 这个表示了有两个装饰器的函数,那么表示的含义为:func = deco2(deco1(func)) 无参装饰器语法如下:

  • C++子类父类成员函数的覆盖和隐藏实例详解

    C++子类父类成员函数的覆盖和隐藏实例详解 函数的覆盖 覆盖发生的条件: (1) 基类必须是虚函数(使用virtual 关键字来进行声明) (2)发生覆盖的两个函数分别位于派生类和基类 (3)函数名和参数列表必须完全相同 函数的隐藏 隐藏发生的条件: (1)子类和父类的函数名相同,参数列表可以不一样 看完下面的例子就明白了 #include "iostream" using namespace std; class CBase{ public: virtual void xfn(int

  • C++ 中函数重载、覆盖与隐藏详解

    C++ 中函数重载.覆盖与隐藏详解 在C++语言中,函数扮演着很重要的角色,不管面向过程设计,还是基于对象设计:不管是面向对象编程,还是基于泛型编程,函数都可以随处而见.在谈论C++中的函数重载.覆盖和隐藏之前,先回顾下函数的基础知识. 函数的声明包括函数的返回值类型,函数名称,参数列表(参数的类型.参数的个数.参数的顺序).例如,声明一个两个整数之和的函数,int iAdd(int iNum1,int iNum2);而函数的定义可以理解为对函数功能的详尽而准确的解说,通俗点,就是实现函数"ho

  • python中函数默认值使用注意点详解

    当在函数中定义默认值时,值初始化只会进行一次,就是执行到def methodname时执行.看下面代码: from datetime import datetime def test(t=datetime.today()): print t if __name__ == "__main__": test() test() 两次方法调用输出的时间都为同一个值,而不是我们预想当前执行时间.对于上面这种情况,建议用下面的方式实现: from datetime import datetime

随机推荐