文章参考<零声教育>的C/C++linux服务期高级架构系统教程,学习笔记。
参考:C++中智能指针的模板类
- 在一个类中,存在指向另一个类对象的指针
- 重载指针运算符(比如:->,*),利用当前类的对象(通过指针运算符)操纵另一个类的成员
- 在析构函数中定义了delete操作,借助于变量的作用域,实现类对象空间的自动释放
- #include
对指针做了封装,让它更好用;实质是一个对象,行为表现的却像一个指针。
🔺 内存泄漏?
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。——百度百科
🔺 栈内存,堆内存
栈: 连续存储;
堆: 非连续的树形存储;
栈内存: 程序自动向操作系统申请分配及回收,速度快使用方便(如函数参数、局部变量、临时变量等,const局部变量也存储在栈区中)。
堆内存: 需要手动申请和释放(new delete),速度较慢、地址不连续。
深拷贝: 增加了一个指针并申请了一个新内存,新指针指向新内存(获得了一个对象的复制实体)
前拷贝: 增加一个指针指向已存在的内存地址(引用?),应用在只读场景下性能更好?
裸指针 = 原始指针
悬空指针 = 被释放内存的指针
(指向某一内存,然后内存被回收了,但它还指着那里……不知道会发生什么了!)
野指针 = 不知道指向哪里的指针(定义的时候未初始化)
函数 | 用法 |
---|---|
s.get() | 返回 shared_ptr 中保存的裸指针谨慎使用,不要保存也不要delete! |
s.reset(…) | 重置 shared_ptr 带参数时,减少引用计数并指向新对象 |
s.use_count() | 返回 shared_ptr 的强引用计数 |
s.unique() | 若 use_count() 为1,返回true,否则false |
make_shared(value) | 优先用这个构造智能指针,更高效 |
std::shared_ptr p(new int(1), DeleteIntPtr) | 指定删除器(就是写个删除函数)管理非new对象、没有析构函数的类、动态数组时(share_ptr的默认删除器不支持数组对象) |
s.shared_from_this() | 返回this指针 |
function(shared_ptr<int>(new int), g()); //如果左->右:new int -> g() -> shared_ptr, g()异常则可能导致内存泄漏
//先创建智能指针,再定义函数
std::shared_ptr<int> p(new int);
function(p, g());
return shared_ptr<A>(this); //no!
return shared_from_this(); //yes!大概就是让两个指向同一个原始指针的shared_ptr的引用计数都正确……更多的学无余力看源码了
use_count() | 获取当前观察资源的引用计数 |
---|---|
expired() | 判断所观察资源是否已经释放(true=已经释放) |
lock() | |
(先lock再ex) | 从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源; |
当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。 |
(配合之前的图食用)
std::unique_ptr ptr(new int[10]);
std::unique_ptr ptr(new int(1), [](int *p){delete p;});
参考:https://blog.csdn.net/weixin_42142630/article/details/121165649
- 共享引用计数的不同的share_ptr被多个线程写,是线程安全的。
- 同一个shared_ptr被多个线程写,不是线程安全的。
- 同一个shared_ptr被多个线程读,是线程安全的。
线程安全:多线程操作一个共享数据的时候,保证所有线程的行为是符合预期的则称为线程安全。
int &ref_a = a;
。const
左值引用可以指向右值,不会修改指向值。int &&ref_a_right = std::move(a);
std::move(),强制转换左右值,无性能提升。
如
int &&ref = std::move(a);
直接声明的ref
是左值,move
返回的int &&
是右值。
右值引用的主要目的:避免深拷贝,优化性能
class A
{
public:
A() :m_ptr(new int(0)){
std::cout << "constructor A" <<std::endl;
} //默认构造函数是浅拷贝,导致main中的a和get返回的b指向同一个指针地,析构时重复释放
// A(const A& a) :m_ptr(new int(*a.m_ptr)){
// std::cout << "copy constructor A" << std::endl;
// } //提供深拷贝的拷贝构造函数,来避免重复删除同一个指针
// A(A&& a) :m_ptr(a.m_ptr){
// a.m_ptr = nullptr; //防止a析构时delete data,提前置空其m_ptr
// std::cout << "move constructor A" << std::endl;
// } //浅拷贝的移动构造函数
~A(){
std::cout << "destructor A, m_ptr: " << m_ptr << std::endl;
delete m_ptr;
m_ptr = nullptr;
}
private:
int* m_ptr; //A是含有堆内存的类
};
A Get(bool flag)
{
A a;
A b;
std::cout << "ready return" <<std::endl;
if(flag) return a;
else return b;
}
int main()
{
{
A a = Get(false);
}
std::cout << "main finish" << std::endl;
return 0;
}
/*
int main(){
MyString a;
a = MyString("Hello Lizzy!"); //move assignment
Mystring c = std::move(a); //move constructor
std::vector vec;
vec.push_back(MyString("World")); //move constructor
}
(std::move()
例程见讲义)
使得参数在传递过程中能够保持其值属性(左值变右值)
int &&a = 10;
int &&b = a; //错误
int &&b = std::forward<int>(a); //正确
[捕获列表](参数列表)->返回类型{函数体}
,返回类型可以不指定(一个return情况下)
参考:基础篇:Lambda 表达式和函数对象
(让匿名函数能够使用外部变量=捕获)
lambda函数的形式参数可以使用auto关键字来产生意义上的泛型:auto add = [](auto x, auto y) { return x+y; };
使用mutable
修饰,使得lambda可以修改值捕获时的变量值:auto ff = [v]() mutable { return ++v; };