赞
踩
当我尝试在\JUCE\extras\WindowsDLL\Builds\VisualStudio2022目录下编译JUCE库的时候,提示报错如下:
报错提示如下:
这里涉及到两个问题
一、这个std::move是干嘛用的
二、为什么这里会报错?
另外,我在实际的开发过程中发现这个JUCE没法编译成一个单独的库的形式,而是只能以.cpp文件和.h文件导入引用的形式使用,如果有人知道如何编译成dll,可以和我交流。
我们百度可以搜到,std::move是做了移动语义,什么是移动语义?它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值。那么什么是左值右值?还需要一步步娓娓道来。
C++中左值(lvalue)和右值(rvalue)是比较基础的概念,虽然平常几乎用不到,但C++11之后变得十分重要,它是理解 move/forward 等新语义的基础。
左值和右值主要的区别之一是左值可以被修改,而右值不能。
可以参考如下一段代码:
int a, b; // a 为左值
a = 3; // 3 为右值
b = a + 3; // a+3 为右值
b = a; // a是左值
我们说到引用的时候,大部分都是在说左值引用
或者一般情况下,也就是绑定了一个会随着右值变化而变化的值,比如:
int x = 6; // x是左值,6是右值
int &y = x; // 左值引用,y引用x
int &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;
}
在上面这两段代码中,第二行和第三行的执行逻辑不一样。
当我们走到第二行的时候,实际上是做了:
但我们在第三行中则稍做了些改变
这样可以看到,如果使用右值引用的方式,则会直接省去一个拷贝操作的开销,相对的就少了拷贝的内存开销。
如果一个类中涉及到资源管理,用户必须显式提供拷贝构造、赋值运算符重载以及析构函数,否则编译器将会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错,比如:
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中,整个过程,只需要创建一块堆内存即可,既省了空间,又大大提高程序运行的效率。
注意:
- 移动构造函数的参数千万不能设置成const类型的右值引用,因为资源无法转移而导致移动语义失效。
- 在C++11中,编译器会为类默认生成一个移动构造,该移动构造为浅拷贝,因此当类中涉及到资源管理时,用户必须显式定义自己的移动构造。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。