[JUCE]从一个有关右值引用的bug,探幽移动语义

ops/2024/9/25 8:22:50/

一、问题

当我尝试在\JUCE\extras\WindowsDLL\Builds\VisualStudio2022目录下编译JUCE库的时候,提示报错如下:

在这里插入图片描述

报错提示如下:

在这里插入图片描述

这里涉及到两个问题

一、这个std::move是干嘛用的

二、为什么这里会报错?

另外,我在实际的开发过程中发现这个JUCE没法编译成一个单独的库的形式,而是只能以.cpp文件和.h文件导入引用的形式使用,如果有人知道如何编译成dll,可以和我交流。

探幽

一、这个std::move是干嘛用的

我们百度可以搜到,std::move是做了移动语义,什么是移动语义?它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值。那么什么是左值右值?还需要一步步娓娓道来。

左值右值

1.左值右值的概念

C++中左值(lvalue)和右值(rvalue)是比较基础的概念,虽然平常几乎用不到,但C++11之后变得十分重要,它是理解 move/forward 等新语义的基础。

  • 左值是可寻址的变量,有持久性;
  • 右值一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。

左值和右值主要的区别之一是左值可以被修改,而右值不能。

可以参考如下一段代码:

int a, b; 	// a 为左值
a = 3; 		// 3 为右值
b = a + 3;  // a+3 为右值
b = a; 		// a是左值 
2.左值引用和右值引用

我们说到引用的时候,大部分都是在说左值引用

  • 左值引用:引用一个对象;
  • 右值引用:就是必须绑定到右值的引用,C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用。

或者一般情况下,也就是绑定了一个会随着右值变化而变化的值,比如:

int x = 6; 		// x是左值,6是右值
int &y = x; 	// 左值引用,y引用xint &z1 = x * 6; 			// 错误,x*6是一个右值
const int &z2 =  x * 6; 	// 正确,可以将一个const引用绑定到一个右值int &&z3 = x * 6; 	// 正确,右值引用
int &&z4 = x; 		// 错误,x是一个左值

什么意思呢,我们可以写一段简单的代码来演示一下:

int main()
{int x = 114;int c = x * 2;int&& b = x * 2;
}

在上面这两段代码中,第二行和第三行的执行逻辑不一样。

当我们走到第二行的时候,实际上是做了:

  1. 创建了一个临时变量temp = x * 2
  2. 令c拷贝一份temp变量
  3. delete temp变量

但我们在第三行中则稍做了些改变

  1. 创建了一个临时变量temp = x * 2
  2. 令c变为指向 temp 变量 的引用

这样可以看到,如果使用右值引用的方式,则会直接省去一个拷贝操作的开销,相对的就少了拷贝的内存开销。

二、移动语义的概念解决了什么问题?

如果一个类中涉及到资源管理,用户必须显式提供拷贝构造、赋值运算符重载以及析构函数,否则编译器将会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错,比如:

class String
{
public:String(char* str = ""){if (nullptr == str)str = "";_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}String& operator=(const String& s){if (this != &s){char* pTemp = new char[strlen(s._str) + 1];strcpy(pTemp, s._str);delete[] _str;_str = pTemp;}return *this;}String operator+(const String& s){char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];strcpy(pTemp, _str);strcpy(pTemp + strlen(_str), s._str);String strRet(pTemp);return strRet;}~String(){if (_str) delete[] _str;}
private:char* _str;
};
int main()
{String s1("hello");String s2("world");String s3(s1 + s2);return 0;
}

上述代码看起来没有什么问题,但是有一个不太尽人意的地方:

在operator+中:strRet在按照值返回时,必须创建一个临时对象,临时对象创建好之后,strRet就被销毁了,最后使用返回的临时对象构造s3,s3构造好之后,临时对象就被销毁了。仔细观察会发现:strRet、临时对象、s3每个对象创建后,都有自己独立的空间,而空间中存放内容也都相同,相当于创建了三个内容完
全相同的对象,对于空间是一种浪费,程序的效率也会降低,而且临时对象确实作用不是很大,那能否对该种情况进行优化呢?
C++11提出了移动语义概念,即:将一个对象中资源移动到另一个对象中的方式,可以有效缓解该问题。

我们看到 关于+号的函数如下:

String operator+(const String& s){char* pTemp = new char[strlen(_str) + strlen(s._str) + 1];strcpy(pTemp, _str);strcpy(pTemp + strlen(_str), s._str);String strRet(pTemp);return strRet;}

最大的开销出自这句拷贝构造函数:

String strRet(pTemp);

这一句话如果我们不修改原有的这个String类的话,这一句话则是一个拷贝开销,我们都知道拷贝带来的开销是比较大的,所以我们需要解决这个问题。

则需要在其构造函数里面下手:

String(String&& s): _str(s._str)
{s._str = nullptr;
}

因为strRet对象的生命周期在创建好临时对象后就结束了,即将亡值,C++11认为其为右值,在用strRet构造临时对象时,就会采用移动构造,即将strRet中资源转移到临时对象中。而临时对象也是右值,因此在用临时对象构造s3时,也采用移动构造,将临时对象中资源转移到s3中,整个过程,只需要创建一块堆内存即可,既省了空间,又大大提高程序运行的效率。

注意:

  1. 移动构造函数的参数千万不能设置成const类型的右值引用,因为资源无法转移而导致移动语义失效。
  2. 在C++11中,编译器会为类默认生成一个移动构造,该移动构造为浅拷贝,因此当类中涉及到资源管理时,用户必须显式定义自己的移动构造。

http://www.ppmy.cn/ops/34444.html

相关文章

rabbitMQ

RabbitMQ 是一个开源的消息中间件,它实现了 AMQP(Advanced Message Queuing Protocol)协议,是一个可靠的、强大的分布式消息队列系统。 RabbitMQ 的主要特点包括: 可靠性:RabbitMQ 提供了持久化、消息确认…

商场数据库项目MySQL实战(1——44)

我们在学习了MySQL基础语句后,终于迎来了第一次实战项目。 此次项目包含了88张商场可能用到的数据库表单,以便于商场的日常运行,并记录商场的各项数据。 通过表单的设计和建立,商城项目可以有效地管理用户信息、商品信息、订单和…

Redis基本數據結構 ― Set

Redis基本數據結構 ― Set 介紹常用命令範例1. 將元素添加到集合2. 移除指定元素3. 獲取集合包含的所有元素4. 交集5. 聯集6. 差集 介紹 元素不能重複無序集合底層透過hash table實現 常用命令 命令功能SADD將元素添加到集合SREM從集合中移除元素SMOVE將元素從一個集合移動到…

Go语言的包管理工具go mod与之前的GOPATH有什么区别?

在深入探讨Go语言的包管理工具go mod与之前的GOPATH之间的区别之前,我们首先需要理解这两个概念各自的作用和背景。 GOPATH时代 在Go语言早期版本中,GOPATH是一个非常重要的环境变量。它告诉Go工具链在哪里查找你的Go代码、第三方库以及编译后的二进制…

git 配置相关

问题一:ssh-keygen -t ed25519 -C "Gitee SSH Key" 这个命令中的 ed25519 字符是什么意思? ssh-keygen 是一个用于生成SSH密钥的工具,SSH(Secure Shell)是一种网络协议,用于加密方式远程登录和其…

C++——list的特性及使用

list的特性 STL中的list是指带头双向循环列表,list每个元素的存储相互独立,因为其节点存储位置独立不连续,其插入和删除不需要挪动其他元素效率比起vector更高。但也因为存储空间不连续的问题,不能做到和vector一样的随机…

opencv基础篇 ——(十五)多边形与凸边型

概念 在计算机视觉和图像处理中,多边形和凸多边形是常用的几何形状描述方式。它们在描述对象的形状、边界或区域时非常有用。下面我将简要介绍它们的概念和特点: 多边形(Polygon)定义: 多边形是一个平面内的闭合图形&…

spring注解之——@Service

Spring框架中的注解Service用于将类标记为Spring应用程序上下文中的服务组件。它主要用于指示带注释的类充当应用程序中的服务层组件。 以下是关于注释的一些要点Service: 业务逻辑:通常,带有注释的类Service包含业务逻辑。这些类负责封装和…