欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > C++11QT复习 (十六)

C++11QT复习 (十六)

2025/4/17 6:02:23 来源:https://blog.csdn.net/m0_49013185/article/details/147054534  浏览:    关键词:C++11QT复习 (十六)

文章目录

    • Day11 移动语义回顾
      • 一、移动语义基础概念
      • 二、自定义 `String` 类的移动语义实现
        • 输出运算符重载:
      • 三、测试函数:验证移动与拷贝行为
      • 四、左值与右值的补充说明
        • 右值引用作为函数返回值
      • 五、知识总结
        • 如何区分左值与右值?
      • 六、附加说明:关于 `std::move`

Day11 移动语义回顾

一、移动语义基础概念

  • 移动语义:通过“转移资源所有权”而非“复制资源”,提升程序效率,尤其适用于临时对象或大对象的处理。
  • 移动构造函数移动赋值运算符:C++11引入的特殊成员函数,分别用于从右值中构造对象或赋值给现有对象。
  • 与拷贝语义的区别
    • 移动语义不会拷贝内存,只是指针赋值,效率更高。
    • 编译器不会自动生成移动构造和移动赋值,需要手动实现。

二、自定义 String 类的移动语义实现

class String
{friend std::ostream& operator<<(std::ostream& os, const String& rhs);public:// 默认构造函数String(): _pstr(nullptr) {cout << "String()" << endl;}// 构造函数:深拷贝字符串String(const char* pstr): _pstr(new char[strlen(pstr) + 1]()) {cout << "String(const char* pstr)" << endl;strcpy_s(_pstr, strlen(pstr) + 1, pstr);}// 拷贝构造函数:深拷贝另一个 String 对象String(const String& rhs): _pstr(new char[strlen(rhs._pstr) + 1]()) {cout << "String(const String& rhs)" << endl;strcpy_s(_pstr, strlen(rhs._pstr) + 1, rhs._pstr);}// 移动构造函数:资源转移,不再复制字符串内容String(String&& rhs): _pstr(rhs._pstr) {cout << "String(String&& rhs)" << endl;rhs._pstr = nullptr;  // 清空源对象}// 拷贝赋值运算符String& operator=(const String& rhs) {cout << "String& operator=(const String& rhs)" << endl;if (this != &rhs) //1、防止自复制{delete[] _pstr;//2、释放左操作数//3、深拷贝_pstr = new char[strlen(rhs._pstr) + 1]();strcpy_s(_pstr, strlen(rhs._pstr) + 1, rhs._pstr);}//4、返回*thisreturn *this;}// 移动赋值运算符String& operator=(String&& rhs) {cout << "String& operator=(String&& rhs)" << endl;if (this != &rhs)//1、自移动{delete[] _pstr;//2、释放左操作数//3、浅拷贝_pstr = rhs._pstr;rhs._pstr = nullptr;}//返回*thisreturn *this;}~String() {cout << "~String()" << endl;delete[] _pstr;_pstr = nullptr;}private:char* _pstr;
};
输出运算符重载:
std::ostream& operator<<(std::ostream& os, const String& rhs)
{if (rhs._pstr) {os << rhs._pstr;}return os;
}

三、测试函数:验证移动与拷贝行为

void test() {String s1("hello");cout << "s1 = " << s1 << endl;String s2 = s1;  // 拷贝构造cout << "s2 = " << s2 << endl;String s3 = "world";  // 隐式调用构造函数,然后移动构造//上面这条代码有个隐式转换的过程,"world" --> String("world")//String("world")是临时对象/匿名对象cout << "s3 = " << s3 << endl;String s4("hainan");cout << "s4 = " << s4 << endl;s4 = String("shangfa");  // 临时对象移动赋值cout << "s4 = " << s4 << endl;s4 = std::move(s4);  // 自移动,测试保护机制cout << "s4 = " << s4 << endl;//warning!使用已移动的 from 对象: s4 (lifetime.1)。//移动赋值运算符函数中没有判断if(this != &rhs)之前,// 打印s4的值,输出结果为空,因为在输出流重载运算符函数中,已经进行了判空操作,若指向为空,直接执行return cout << "11111" << endl;/*std::move可以将左值转换为右值,实质上没有做任何移动,只是在底层做了强制转换static_cast<T &&>(lvalue)如果以后不想再使用某个左值,可以使用std::mobe将其转换为右值,以后就不再使用了*/s2 = std::move(s1);  // s1 被移动后已失效cout << "s1 = " << s1 << endl;cout << "s2 = " << s2 << endl;
}

四、左值与右值的补充说明

void testRightValue()
{int a = 10;int b = 20;int* pflag = &a;string s1("hello");string s2("world");int& ref = a;//int& ref2 = 10;//error!非常量引用的初始值必须为左值const int& ref2 = 10;/*const左值引用既可以绑定到左值,也可以绑定到右值*/const int& ref3 = a;&a;//左值&b;//左值&pflag;//左值& *pflag;//左值&s1;//左值&s2;//左值//表达式必须为左值或函数指示符//&(a + b);//左值//&(s1 + s2);//左值&ref;//左值//C++11之前是不能识别右值的,C++11之后新增语法可以识别右值int&& rref = 10;//右值引用//int&& rref2 = b;//error!无法将右值引用绑定到左值//还有一个问题,左值引用是左值还是右值?&rref;//右值引用在此处是左值(右值引用在作为函数参数的时候,体现出来的是左值的含义)}//那么,右值引用可以是右值吗?(右值引用作为函数返回类型的时候是右值)
int&& func()
{return 10;
}int main()
{test();//&func();//error!“&”要求左值return 0;
}
右值引用作为函数返回值
int&& func() {return 10;
}

右值引用返回的对象仍然是右值,不能取地址。


五、知识总结

类型是否可绑定左值是否可绑定右值备注
左值引用 (T&)常用于一般变量引用
const 左值引用 (const T&)可以绑定临时对象,常用于函数参数
右值引用 (T&&)主要用于移动语义
如何区分左值与右值?
  • 能否取地址(&) 是判断左值的重要标准。
  • 表达式结果是否具名或可持久存在

六、附加说明:关于 std::move

  • std::move(x) 实质上是 static_cast<T&&>(x),并不真正“移动”。
  • 它只是告诉编译器:“我不再使用这个对象了,可以安全地‘偷走’资源”。
  • 右值的存放位置(冷知识):右值短暂的存在于栈上

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词