C++进阶——C++11新特性

news/2024/11/17 0:04:22/

文章目录

  • 一、move移动语义
    • 右值
      • 右值和右值引用区别
      • 区别总结:
  • 二、forward完美转发
  • 三、move 与 forward
  • 四、智能指针
    • 裸指针
    • 智能指针
      • shared_ptr
      • unique_ptr
      • weak_ptr


一、move移动语义

move(左值):移动语义,得到右值类型 (int&&)a

使用 move(a) 将对象 a 转换为右值引用,表示该对象可以被移动而不是进行深拷贝。这意味着资源所有权将从 a 转移到目标对象。

当将对象进行移动时,通常是为了提高性能,避免不必要的复制开销。例如,如果类A包含了一个动态分配的内存块,通过移动操作可以高效地将这个内存块的所有权从一个对象转移到另一个对象,而不需要复制整个内存块。

class A {
public:A() { /* 构造函数 */ }A(A&& other) { /* 移动构造函数 */ }
};int main() {A a;A b = std::move(a); // 使用移动操作return 0;
}

在这个例子中,通过 std::move(a) 将对象 a 进行移动操作,将资源的所有权从 a 转移到 b。注意,移动操作并不保证 a 的状态仍然有效,它可能处于移后被破坏的状态。

需要注意的是,移动操作后,你应该避免使用已经移动的对象,除非你对其重新赋值或重新构造。

总结起来,move(a) 表示将对象 a 进行移动操作,以提高性能并转移资源的所有权。

右值

右值类型 (int&&)a
右值引用是C++11引入的一种新的引用类型,用 && 表示。

与左值引用不同,右值引用主要用于表示临时对象、将要被移动的对象或被转移所有权的对象。
当你使用 std::move 函数将一个对象传递给移动构造函数、移动赋值运算符或其他接受右值引用参数的函数时,它将告诉编译器你希望该对象的资源所有权被转移,而不是进行常规的拷贝。
在上面的代码中,通过 std::move(a) 将 a 转换为右值引用,并将其作为参数传递给 b 的移动构造函数,即 A(A&& other)
这告诉编译器你希望移动构造函数将 a 的资源所有权移动到 b 中,而不是进行深拷贝。

使用 std::move 可以显式地表明你的意图,提高代码的可读性,并且在适当的情况下利用移动语义,避免不必要的资源复制和内存分配,从而提高性能。
需要注意的是,一旦对象被移动,你应该避免在移动后的继续使用原始对象a,因为其状态不再有效。

右值和右值引用区别

右值(Rvalue)和右值引用(Rvalue reference)是 C++ 中的两个相关但不同的概念。
右值(Rvalue):
右值是一个表达式,它代表一个临时值或可以被移动的值。右值可以出现在赋值操作的右侧,可以被复制或移动,但不能被修改。右值可以是字面量、临时对象、函数返回值等。

以下是一些示例:

int a = 5;          // 5 是一个右值
int b = a + 3;      // a + 3 是一个右值
int&& c = a + 3;    // a + 3 是一个右值

右值引用(Rvalue reference):
右值引用是一种引用类型,用于绑定到右值。它使用 && 符号表示,并允许对右值进行移动语义的操作。右值引用可以用来扩展对象的生命周期,以便进行移动操作。

以下是一个使用右值引用的示例:

int&& rref = 123;  // 创建一个右值引用

右值引用通常与移动语义一起使用,以实现高效的资源转移和避免不必要的复制操作。

区别总结:

右值是一个表达式,代表一个临时值或可移动的值。
右值引用是一种引用类型,用于绑定到右值,允许对右值进行移动语义的操作。
需要注意的是,通过使用 std::move 可以将一个左值转换为右值引用,以便进行移动操作。例如:std::move(a) 将左值 a 转换为右值引用。

二、forward完美转发

forward:类型完美转发,能够识别左值和右值类型

完美转发(Perfect Forwarding)是一种用于在泛型编程中传递参数的技术,它允许将参数按照原始类型和值转发给其他函数,而不改变其类型或值。在 C++ 中,我们使用 std::forward 函数来实现完美转发。

#include <iostream>
#include <utility>void someFunction(int& value) {std::cout << "L-value reference: " << value << std::endl;
}void someFunction(int&& value) {std::cout << "R-value reference: " << value << std::endl;
}template <typename T>
void forwardExample(T&& arg) {someFunction(std::forward<T>(arg)); // 使用完美转发
}int main() {int value = 42;forwardExample(value); // 传递左值forwardExample(123); // 传递右值return 0;
}

输出
L-value reference: 42
R-value reference: 123

三、move 与 forward

左值:能在内存中取得地址,即是左值 左值持久 左值通常是变量
右值:在内存中无法取得地址的是右值 右值短暂 右值通常是表达式求值过程中创建的临时对象
左值引用:绑定到确定存储空间以及变量名的对象上,表达式结束后对象依然存在
右值引用:绑定到要求转换的表达式、字面常量、返回右值的临时对象上,表达式结束后对象就会销毁

move移动语义的基本实现原理:
引用折叠
左值引用 + 左值引用 = 左值引用
左值引用 + 右值引用 = 左值引用
右值引用 + 左值引用 = 左值引用
右值引用 + 右值引用 = 右值引用

move:使用移动语义,强制把一个左值转为右值,减少了一次临时对象的创建,提升效率
forward:完美转发 在函数模板中,完全按照模板参数的类型传递给调用的另一个函数 ,侦测是左值还是右值,然后进行对应类型的转发

#include<iostream>using namespace std;void other_func(int& a) {cout << "int&" << endl;
}void other_func(int&& a) {cout << "int&&" << endl;
}void other_func(const int& a) {cout << "const int&" << endl;
}void other_func(const int&& a) {cout << "const int&&" << endl;
}template<typename T>
void func(T&& t) {other_func(forward<T>(t));
}
int main() {int a = 0;int b = 0;const int c = 0;const int d = 0;other_func(a);other_func(move(b));other_func(c);other_func(move(c));return 0;
}

输出结果:

int&
int&&
const int&
const int&&

myvectormystring.cpp

四、智能指针

裸指针

#include <iostream>
using namespace std;int main() {//裸指针//data段 heap堆 Stack栈int *p = new int(10);cout << *p << endl;delete p;   // 显式释放内存return 0;
}

内存泄露:
(1)没有delete
(2)delete前return掉了
(3)代码直接异常

智能指针

在C++中,智能指针是一种用于管理动态分配的内存的智能化工具。它们通过封装指针并提供自动内存管理功能,可以帮助减少内存泄漏和悬挂指针等问题,保证能够做到资源的自动释放

原理:利用栈上的对象出作用域自动析构的特征,来做到资源的自动释放

核心:智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存

shared_ptr

共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针 shared_ptr 也是一个模板类,如果要进行初始化有三种方式:通过构造函数、std::make_shared辅助函数以及reset方法。共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数use_count

#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass constructor" << std::endl;}MyClass(int in) {a = in;std::cout << "有参 MyClass constructor" << std::endl;}~MyClass() {std::cout << "MyClass destructor" << std::endl;}int a;
};int main() {std::shared_ptr<MyClass> ptr1(new MyClass(10));std::shared_ptr<MyClass> pmake1 = std::make_shared<MyClass>(20);{std::shared_ptr<MyClass> ptr2 = ptr1;std::shared_ptr<MyClass> pmake2 = pmake1;std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;std::cout << "ptr2 use count: " << ptr2.use_count() << std::endl;std::cout << "pmake1 use count: " << pmake1.use_count() << std::endl;std::cout << "pmake2 use count: " << pmake2.use_count() << std::endl;}std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;std::cout << "pmake1 use count: " << pmake1.use_count() << std::endl;std::cout << ptr1->a << std::endl;return 0;
}

输出

有参 MyClass constructor
有参 MyClass constructor
ptr1 use count: 2
ptr2 use count: 2
pmake1 use count: 2
pmake2 use count: 2
ptr1 use count: 1
pmake1 use count: 1
10
MyClass destructor
MyClass destructor

unique_ptr

独占智能指针:std::unique_ptr的作用是管理动态分配的对象,并在不再需要时自动释放相关资源。它实现了独占所有权的语义,即同一时间只能有一个std::unique_ptr拥有对资源的所有权,从而避免资源的多重释放和内存泄漏。

独占智能指针适用于以下情况:

  • 动态分配单个对象:当需要动态分配单个对象时,且希望在不再需要时自动释放内存,可以使用std::unique_ptr。它确保了在对象不再需要时(例如作用域结束、函数返回等),所分配的内存将被正确地释放。
  • 动态分配数组:std::unique_ptr还可以用于动态分配数组,并在不再需要时释放数组内存。但需要指定一个自定义的删除器(deleter),以确保正确释放数组内存。可以使用lambda函数、函数指针或函数对象作为删除器。
#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass constructor" << std::endl;}MyClass(int a) : value(a) {std::cout << "param MyClass constructor" << std::endl;}~MyClass() {std::cout << "MyClass destructor" << std::endl;}void DoSomething() {std::cout << "Doing something..." << std::endl;}int value;
};int main() {std::unique_ptr<MyClass> ptr(new MyClass());ptr->DoSomething();return 0;
}

weak_ptr

弱引用智能指针可以看做是shared_ptr的助手,用于指向shared_ptr所管理的对象,但不拥有该对象的所有权。它不管理shared_ptr内部的指针。std::weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视shared_ptr中管理的资源是否存在。
当shared_ptr被销毁时,weak_ptr会自动失效,指向的对象也会被释放。

可以使用weak_ptr.lock()方法获取原始指针。

#include <iostream>
#include <memory>using namespace std;int main() {shared_ptr<int> sp = make_shared<int>(10);weak_ptr<int> wp = sp;cout << "sp count = " << sp.use_count() << endl;cout << "wp count = " << wp.use_count() << endl;if (auto p = wp.lock()) {cout << "wp points to " << *p << endl;} else {cout << "wp is expired" << endl;}sp.reset();  // 使其引用计数为0cout << "sp count = " << sp.use_count() << endl;cout << "wp count = " << wp.use_count() << endl;if (auto p = wp.lock()) {cout << "wp points to " << *p << endl;} else {cout << "wp is expired" << endl;}return 0;
}

输出

sp count = 1
wp count = 1
wp points to 10
sp count = 0
wp count = 0
wp is expired

分享一段指定删除器的代码

shared_ptr<Test> ppp(new Test(100), [](Test* t) {cout << "Test对象的内存被释放了......." << endl;delete t;});

http://www.ppmy.cn/news/472382.html

相关文章

22JS13——简单类型与复杂类型

文章目录 一、简单类型与复杂类型二、堆和栈三、简单类型的内存分配四、复杂类型的内存分配五、简单类型传参六、复杂类型传参 目标&#xff1a; 1、简单类型与复杂类型 2、堆和栈 3、简单类型的内存分配 4、复杂类型的内存分配 5、简单类型传参 6、复杂类型传参 一、简单类型与…

在Centos Stream 9上Docker的实操教程(七) - Docker上实现MYSQL实现主从复制

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

修罗论坛xiuno源码在线搭建

Xiuno BBS 是一款小巧、稳定、支持在大数据量下仍然保持高负载能力的轻论坛。它只有 20 多个表&#xff0c;源代码压缩后 1M 左右&#xff0c;运行速度非常快&#xff0c;处理单次请求在 0.01 秒级别&#xff0c;在有 APC、Yac、XCache 的环境下可以跑到 0.00x 秒&#xff0c;对…

220v 中间可以直接接多大多少瓦的电阻

电压的平方除以电阻的瓦数等于可以接用的电阻值. 要直接接220V如2W电阻,电阻值R≮220/224.2KΩ. 要直接接220V如4W电阻,电阻值R≮220/412.1KΩ. 要直接接220V如6W电阻V,电阻值R≮220/68.07KΩ. 要直接接220V如10W电阻V,电阻值R≮220/104.84KΩ.

电线的安全载流量是多少?常用电线能带多少功率的电器?

&#xff08;1&#xff09;1平方米的铜铜芯电缆&#xff0c;安全性电缆载流量范畴为&#xff08;6-8&#xff09;A&#xff0c;能够推动的输出功率范畴为&#xff08;1.3-1.7&#xff09;KW。 &#xff08;2&#xff09;1.5平方米的铜铜芯电缆&#xff0c;安全性电缆载流量范畴…

关于python浮点数类型错误的是_关于Python的浮点数类型,以下选项中描述错误的是...

【单选题】在负载为三角形连接的对称三相电路中,各线电流与相电流的关系是 【单选题】下列关系式不成立的是 【单选题】某电阻元件的额定数据为“ 1K Ω 、 2.5W ”,正常使用时允许流过的最大电流为 【单选题】感接在有效值为 2V 的交流电压源两端,已知吸收 Q1var ,则该电感的感…

关于python字符串以下描述错误的是_以下关于 Python 字符串的描述中,错误的是

【填空题】电路的三种工作状态是 、短路 。 【单选题】两个阻值相同的电阻器串联后的等效电阻与并联后的等效电阻之比是 【单选题】通过电感 L 的电流为 ,此时电感的端电压 U L 2.4V ,则电感 L 为 【单选题】通常高压输电时,三相负载都是对称负载,应采用的输电线路是 【单选题】…

NFA纽福克斯500W逆变电源(车载12V转220V数码显示USB接口) 630元 4006830990

商品名称&#xff1a;NFA纽福克斯500W逆变电源&#xff08;车载12V转220V数码显示USB接口&#xff09; 630元 4006830990 品牌&#xff1a;纽福克斯 型号&#xff1a;7735 功率&#xff1a;500W 比旧款新增特点&#xff1a; 1、数码输出功率及电瓶电压显示功能 2、数码…