文章目录
- 一、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;});