建立网站解析会员视频是犯什么罪适合中层管理的培训
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:46
当前位置: 首页 > news >正文
建立网站解析会员视频是犯什么罪,适合中层管理的培训,免费做网站哪个好,设计精美的国外网站文章目录 C115.右值引用和移动语义5.1左值引用和右值引用5.2左值引用与右值引用比较5.3右值引用使用场景和意义5.4右值引用引用左值及其一些更深入的使用场景分析5.5完美转发 6.新的类功能 C11 5.右值引用和移动语义 右值引用是C11引入的一个新特性#xff0c;用于支持移动语义… 文章目录 C115.右值引用和移动语义5.1左值引用和右值引用5.2左值引用与右值引用比较5.3右值引用使用场景和意义5.4右值引用引用左值及其一些更深入的使用场景分析5.5完美转发 6.新的类功能 C11 5.右值引用和移动语义 右值引用是C11引入的一个新特性用于支持移动语义和完美转发。 在C中左值和右值是根据它们在表达式中的位置来定义的。左值是指在表达式中位于赋值符号左侧的对象而右值是指位于赋值符号右侧的对象。例如在表达式 a b 中a 是左值b 是右值。 在C11之前左值和右值都是使用普通的引用T来处理的。但是这在处理某些情况时会造成一些不便例如在实现移动构造函数和移动赋值运算符时。 为了解决这个问题C11引入了右值引用T的概念。右值引用是一种特殊的引用类型只能绑定到右值上。这使得我们可以实现移动语义即将资源从一个对象移动到另一个对象而不是进行深拷贝。 右值引用还可以用于实现完美转发即在函数模板中将参数以原样传递给其他函数保持其原始的左值或右值性质不变。这需要使用 std::forward 函数来实现。 5.1左值引用和右值引用 什么是左值什么是右值 左值和右值是根据它们在表达式中的位置来定义的。 左值是指可以位于赋值符号左侧的对象它表示一个对象的身份例如一个具有名称的变量。左值必须在内存中有实体可以使用取地址符获取其地址。 右值指的是位于赋值符号右侧的对象它表示一个对象的值。右值可以是临时对象临时变量也可以是在内存或CPU寄存器中的值。当一个对象被用作右值时使用的是它的内容值而不是它的身份。 简单来说左值和右值的区别在于左值表示一个对象的身份而右值表示一个对象的值。左值可以取地址右值不能取地址。 左值示例
int a 10; // a 是左值因为它是一个具有名称的变量可以在赋值符号左侧使用
int b a; // a 是左值因为它在赋值符号左侧右值示例 在这个例子中a b 的结果是一个临时值没有具体的身份变量名只能在赋值符号右侧使用因此它是右值。
int a 10;
int b 20;
int c a b; // a b 的结果是右值因为它是一个临时值没有具体的身份变量名什么是左值引用什么是右值引用 左值引用lvalue reference就是对一个左值进行引用的类型。 具体来说它是一个具有名称的变量的别名可以通过该别名访问和修改该变量的值。左值引用使用 T 表示其中 T 是被引用的变量的类型。 右值引用rvalue reference就是对一个右值进行引用的类型。 由于右值通常不具有名字我们也只能通过引用的方式找到它的存在。右值引用使用 T 表示其中 T 是被引用的变量的类型。 左值引用示例 左值是一个表示数据的表达式(如变量名或解引用的指针)我们可以获取它的地址可以对它赋值左值可以出现赋值符号的左边右值不能出现在赋值符号左边。 定义时const修饰符后的左值不能给他赋值但是可以取它的地址。左值引用就是给左值的引用给左值取别名。
int main()
{// 以下的p、b、c、p都是左值int p new int(0);int b 1;const int c 2;// 以下几个是对上面左值的左值引用int* rp p;int rb b;const int rc c;int pvalue *p;return 0;
}右值引用示例 右值也是一个表示数据的表达式如字面常量、表达式返回值函数返回值(这个不能是左值引用返回)等等右值可以出现在赋值符号的右边但是不能出现出现在赋值符号的左边右值不能取地址。 右值引用就是对右值的引用给右值取别名。
int main()
{double x 1.1, y 2.2;// 以下几个都是常见的右值10;x y;fmin(x, y);// 以下几个都是对右值的右值引用int rr1 10;double rr2 x y;double rr3 fmin(x, y);// 这里编译会报错error C2106: “”: 左操作数必须为左值10 1;x y 1;fmin(x, y) 1;return 0;
}5.2左值引用与右值引用比较 左值引用总结 1左值引用只能引用左值不能引用右值。 2但是const左值引用既可引用左值也可引用右值
int main()
{// 左值引用只能引用左值不能引用右值。int a 10;int ra1 a; // ra为a的别名//int ra2 10; // 编译失败因为10是右值// const左值引用既可引用左值也可引用右值。const int ra3 10;const int ra4 a;return 0;
}右值引用总结 1右值引用只能右值不能引用左值。 2但是右值引用可以move以后的左值。
int main()
{// 右值引用只能右值不能引用左值。int r1 10;// error C2440: “初始化”: 无法从“int”转换为“int ”// message : 无法将左值绑定到右值引用int a 10;int r2 a;// 右值引用可以引用move以后的左值int r3 std::move(a);return 0;
}5.3右值引用使用场景和意义 左值引用的场景
1做参数 当函数需要传递大的对象或复杂的数据结构时可以使用左值引用来做参数传递以避免对象的复制。这样可以提高函数的执行效率并减少内存占用。例如 在这个例子中ComplexObject 是一个复杂的数据结构使用左值引用来做参数传递可以避免对象的复制。
void foo(const ComplexObject obj) { // …
}2做返回值 当函数需要返回一个大的对象或复杂的数据结构时可以使用左值引用来做返回值以避免对象的复制。这样可以提高函数的执行效率并减少内存占用。例如 在这个例子中bar 函数返回一个 ComplexObject 类型的左值引用以避免对象的复制。由于返回的是一个引用调用者可以直接访问原始对象而不是一个复制的副本。
ComplexObject bar() { static ComplexObject obj; return obj;
}综上无论是做参数还是做返回值它们都可以减少拷贝提高效率。 以String的实现为例 左值引用被用于实现字符串类的拷贝构造函数和赋值重载运算符。 在拷贝构造函数中左值引用被用来接收一个传入的字符串对象const string s这样可以避免对象的复制。通过创建一个临时对象string tmp(s._str)然后使用 swap 函数将临时对象的资源交换到当前对象实现了深拷贝的效果。这里的左值引用作为参数传递方式使得我们可以直接访问传入的字符串对象的成员变量。 在赋值重载运算符中左值引用同样被用来接收一个传入的字符串对象const string s。这里的左值引用的作用是使得我们可以将返回的对象赋值给当前对象。通过创建一个临时对象string tmp(s)然后使用 swap 函数将临时对象的资源交换到当前对象实现了深拷贝的效果。最后返回当前对象的引用return *this使得赋值操作可以连续进行。 综上在这个代码片段中左值引用被用于实现深拷贝的拷贝构造函数和赋值重载运算符它使得我们可以直接访问传入的对象避免对象的复制提高了程序的性能。
// s1.swap(s2)
void swap(string s)
{::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}// 拷贝构造
string(const string s):_str(nullptr)
{cout string(const string s) – 深拷贝 endl;string tmp(s._str);swap(tmp);
}// 赋值重载
string operator(const string s)
{cout string operator(string s) – 深拷贝 endl;string tmp(s);swap(tmp);return this;
}左值引用的短板 但是当函数返回对象是一个局部变量出了函数作用域就不存在了就不能使用左值引用返回只能传值返回。 下面是一个代码示例演示了左值引用的短板
class String {
public: String(const char str ) : _str(nullptr) { std::cout String(const char* str) – 深拷贝 std::endl; _str new char[strlen(str) 1]; strcpy(_str, str); } String(const String other) : _str(nullptr) { std::cout String(const String other) – 深拷贝 std::endl; _str new char[strlen(other._str) 1]; strcpy(_str, other._str); } String operator(const String other) { std::cout String operator(const String other) – 深拷贝 std::endl; if (this ! other) { delete[] _str; _str new char[strlen(other._str) 1]; strcpy(_str, other._str); } return this; } ~String() { delete[] _str; } private: char _str;
}; void printString(const String str)
{ std::cout printString(const String str) std::endl; std::cout str._str std::endl;
} int main()
{ String s1(Hello); String s2 World; printString(s1 s2); // 这里会创建一个临时对象无法用左值引用捕获 return 0;
}在上面的代码中我们定义了一个简单的 String 类并实现了拷贝构造函数、赋值重载运算符和析构函数。在 main 函数中我们创建了两个 String 对象 s1 和 s2然后调用 printString 函数打印它们的拼接结果。 然而在 printString 函数中我们尝试通过左值引用来捕获传入的字符串对象但是这里的传入参数实际上是一个临时对象由 s1 s2 创建无法用左值引用捕获。这就是左值引用的短板之一它无法绑定到临时对象或右值上导致我们无法直接访问这些对象的资源。为了解决这个问题C11引入了右值引用的概念。
右值引用和移动语义解决上述问题 使用右值引用和移动语义可以解决上述问题。具体做法是在 String 类中实现移动构造函数和移动赋值运算符利用右值引用来捕获临时对象并将其资源移动到当前对象中避免深拷贝。 下面是修改后的代码示例 我们在 String 类中实现了移动构造函数和移动赋值运算符使用右值引用来捕获临时对象并将其资源移动到当前对象中。在 main 函数中临时对象就可以使用右值引用捕获以便调用移动构造函数和移动赋值运算符。这样就可以避免深拷贝提高程序的性能。
class String {
public: String(const char* str ) : _str(nullptr) { std::cout String(const char* str) – 深拷贝 std::endl; _str new char[strlen(str) 1]; strcpy(_str, str); } String(const String other) : _str(nullptr) { std::cout String(const String other) – 深拷贝 std::endl; _str new char[strlen(other._str) 1]; strcpy(_str, other._str); } String(String other) : _str(nullptr) { std::cout String(String other) – 移动构造 std::endl; _str other._str; other._str nullptr; } String operator(const String other) { std::cout String operator(const String other) – 深拷贝 std::endl; if (this ! other) { delete[] _str; _str new char[strlen(other._str) 1]; strcpy(_str, other._str); } return *this; } String operator(String other) { std::cout String operator(String other) – 移动赋值 std::endl; if (this ! other) { delete[] _str; _str other._str; other._str nullptr; } return this; } ~String() { delete[] _str; } private: char _str;
}; void printString(const String str)
{ std::cout printString(const String str) std::endl; std::cout str._str std::endl;
} int main()
{ String s1(Hello); String s2 World; printString(s1 s2); // 临时对象可以使用右值引用捕获 return 0;
}5.4右值引用引用左值及其一些更深入的使用场景分析 按照语法右值引用只能引用右值但右值引用一定不能引用左值吗 但是有些场景下可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时可以通过move函数将左值转化为右值。 C11中std::move()函数位于 头文件中该函数可以将一个左值强制转化为右值引用然后实现移动语义。
templateclass _Ty
inline typename remove_reference_Ty::type move(_Ty _Arg) _NOEXCEPT
{// forward _Arg as movablereturn ((typename remove_reference_Ty::type)_Arg);
}int main()
{bit::string s1(hello world);// 这里s1是左值调用的是拷贝构造bit::string s2(s1);// 这里我们把s1 move处理以后, 会被当成右值调用移动构造// 但是这里要注意一般是不要这样用的因为我们会发现s1的// 资源被转移给了s3s1被置空了。bit::string s3(std::move(s1));return 0;
}move函数使用示例
void push_back (value_type val);
int main()
{listbit::string lt;bit::string s1(1111);// 这里调用的是拷贝构造lt.push_back(s1);// 下面调用都是移动构造lt.push_back(2222);lt.push_back(std::move(s1));return 0;
}// string(const string s) – 深拷贝
// string(string s) – 移动语义
// string(string s) – 移动语义5.5完美转发 完美转发Perfect Forwarding是C11引入的一种特性它允许函数模板按照参数原来的形式类型、值类别将参数转发给其他函数从而实现更灵活的编程。 完美转发的核心在于使用 std::forward 函数模板它可以将左值转换为左值引用将右值转换为右值引用从而实现参数的完美转发。下面是一个简单的示例
templatetypename F, typename T1, typename T2
void flip(F f, T1 t1, T2 t2)
{ f(std::forwardT2(t2), std::forwardT1(t1));
}在这个示例中flip 函数模板接受一个函数对象 f 和两个参数 t1 和 t2并将它们转发给函数对象 f但是转发的顺序与原始顺序相反。通过使用 std::forward我们可以保证参数的类型和值类别得到正确的保留和转发。 完美转发在很多情况下都非常有用比如在实现代理函数、工厂函数、回调函数等场景中它可以让我们更灵活地处理参数并避免不必要的拷贝或移动操作。 完美转发在传参的过程中保留对象原生类型属性
void Fun(int x){ cout 左值引用 endl; }
void Fun(const int x){ cout const 左值引用 endl; }
void Fun(int x){ cout 右值引用 endl; }
void Fun(const int x){ cout const 右值引用 endl; }// std::forwardT(t)在传参的过程中保持了t的原生类型属性。
templatetypename T
void PerfectForward(T t)
{Fun(std::forwardT(t));
}int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}完美转发的示例
void print(int i) { std::cout rvalue: i std::endl;
} void print(const int i) { std::cout lvalue: i std::endl;
} templatetypename T
void forwardPrint(T t) { print(std::forwardT(t));
} int main() { int i 42; forwardPrint(i); // lvalue: 42 forwardPrint(std::move(i)); // rvalue: 42 return 0;
}6.新的类功能 默认成员函数 原来C类中有6个默认成员函数 1构造函数 2析构函数 3拷贝构造函数 4拷贝赋值重载 5取地址重载 6const 取地址重载 C11 新增了两个移动构造函数和移动赋值运算符重载。 针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下 1如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。 默认生成的移动构造函数对于内置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动构造如果实现了就调用移动构造没有实现就调用拷贝构造。 2如果你没有自己实现移动赋值重载函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个那么编译器会自动生成一个默认移动赋值。 默认生成的移动构造函数对于内置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动赋值如果实现了就调用移动赋值没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似) 3如果你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。 强制生成默认函数的关键字default: C11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数但是因为一些原因这个函数没有默认生成。比如我们提供了拷贝构造就不会生成移动构造了那么我们可以使用default关键字显示指定移动构造生成。
class Person
{
public:Person(const char* name , int age 0):_name(name), _age(age){}Person(const Person p):_name(p._name),_age(p._age){}Person(Person p) default;private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 s1;Person s3 std::move(s1);return 0;
}禁止生成默认函数的关键字delete: 如果能想要限制某些默认函数的生成在C98中是该函数设置成private并且只声明补丁这样只要其他人想要调用就会报错。在C11中更简单只需在该函数声明加上delete即可该语法指示编译器不生成对应函数的默认版本称delete修饰的函数为删除函数。
class Person
{
public:Person(const char* name , int age 0):_name(name), _age(age){}Person(const Person p) delete;private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 s1;Person s3 std::move(s1);return 0;
}
- 上一篇: 建立网站接受投注是什么意思网站功能调研
- 下一篇: 建立网站企业抚顺市营商环境建设局网站
相关文章
-
建立网站接受投注是什么意思网站功能调研
建立网站接受投注是什么意思网站功能调研
- 技术栈
- 2026年03月21日
-
建立网站基本步骤广西建设厅网证件查询
建立网站基本步骤广西建设厅网证件查询
- 技术栈
- 2026年03月21日
-
建立网站第一步是建立什么wordpress模版下载
建立网站第一步是建立什么wordpress模版下载
- 技术栈
- 2026年03月21日
-
建立网站企业抚顺市营商环境建设局网站
建立网站企业抚顺市营商环境建设局网站
- 技术栈
- 2026年03月21日
-
建立网站容量国外服务器厂商
建立网站容量国外服务器厂商
- 技术栈
- 2026年03月21日
-
建立网站容量图书馆网页设计素材
建立网站容量图书馆网页设计素材
- 技术栈
- 2026年03月21日
