C++智能指针、绑定器和函数对象、lambda表达式

智能指针

​ 智能指针可以保证资源的自动释放

不带引用计数的智能指针

auto_ptr只让最后一个指向的指针管理资源,之前的auto_ptr会被置为nullptr

scoped_ptr删除了拷贝构造和赋值重载函数

unique_ptr:推荐使用,也删除了拷贝构造和赋值重载函数,但是提高了右值引用的拷贝构造和赋值重载

unique_ptr<int> ptr(new int);
unique_ptr<int> ptr2 = std::move(ptr); // 使用了右值引用的拷贝构造
ptr2 = std::move(ptr); // 使用了右值引用的operator=赋值重载函数

语义明确

带引用计数的智能指针

shared_ptr和weak_ptr标记几个在使用该指针的数量shared_ptr和weak_ptr底层的引用计数已经通过CAS操作,保证了引用计数加减的原子特性,因此shared_ptr和weak_ptr本身就是线程安全的带引用计数的智能指针

​ shared_ptr:强智能指针可以改变资源的引用技术

​ weak_ptr:若智能指针 不会改变资源的引用技术

强智能指针的交叉引用是什么问题?什么结果?怎么解决?

#include <iostream>
#include <memory>
using namespace std;
class B; // 前置声明类B
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
shared_ptr<B> _ptrb; // 指向B对象的智能指针
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
shared_ptr<A> _ptra; // 指向A对象的智能指针
};
int main()
{
shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
ptra->_ptrb = ptrb;// A对象的成员变量_ptrb也指向B对象,B的引用计数为2
ptrb->_ptra = ptra;// B对象的成员变量_ptra也指向A对象,A的引用计数为2
cout << ptra.use_count() << endl; // 打印A的引用计数结果:2
cout << ptrb.use_count() << endl; // 打印B的引用计数结果:2
/*
出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
B对象的引用计数从2减到1,达不到释放A和B的条件(释放的条件是
A和B的引用计数为0),因此造成两个new出来的A和B对象无法释放,
导致内存泄露,这个问题就是“强智能指针的交叉引用(循环引用)问题”
*/
return 0;
}

代码打印结果:
A()
B()
2
2
可以看到,A和B对象并没有进行析构,通过上面的代码示例,能够看出来“交叉引用”的问题所在,就是对象无法析构,资源无法释放,那怎么解决这个问题呢?请注意强弱智能指针的一个重要应用规则:定义对象时,用强智能指针shared_ptr,在其它地方引用对象时,使用弱智能指针weak_ptr

弱智能指针weak_ptr区别于shared_ptr之处在于

  1. weak_ptr不会改变资源的引用计数,只是一个观察者的角色,通过观察shared_ptr来判定资源是否存在
  2. weak_ptr持有的引用计数,不是资源的引用计数,而是同一个资源的观察者的计数
  3. weak_ptr没有提供常用的指针操作,无法直接访问资源,需要先通过lock方法提升为shared_ptr强智能指针,才能访问资源

那么上面的代码怎么修改,也就是如何解决带引用计数的智能指针的交叉引用问题,代码如下:

#include <iostream>
#include <memory>
using namespace std;
class B; // 前置声明类B
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
weak_ptr<B> _ptrb; // 指向B对象的弱智能指针。引用对象时,用弱智能指针
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
weak_ptr<A> _ptra; // 指向A对象的弱智能指针。引用对象时,用弱智能指针
};
int main()
{
// 定义对象时,用强智能指针
shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
ptra->_ptrb = ptrb;
// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
ptrb->_ptra = ptra;
cout << ptra.use_count() << endl; // 打印结果:1
cout << ptrb.use_count() << endl; // 打印结果:1
/*
出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
B对象的引用计数从1减到0,达到释放A和B的条件,因此new出来的A和B对象
被析构掉,解决了“强智能指针的交叉引用(循环引用)问题”
*/
return 0;
}

绑定器和函数对象

bind1st:operator()中第一个形参变量绑定成一个确定的值

bind2st:operator()中第二个形参变量绑定成一个确定的值

绑定器+二元函数对象->一元函数对象

auto it1 = find_if(vec.begin(),vec.end(),
bind1st(greater<int>(),70);) //find_if寻找第一个小于70
void hello1(){
cout << "hello world!"<< endl;
}
function<void> func1 = hello1;
func1();//"hello world!"
function<int(int,int)> func2 = [](int a,int b)->int {return a+b};

function

std::function还有一个特别有意思的用法,你可以将一个重载了()操作符的对象赋值给它,这样就可以像调用函数一样使用该对象了。

...
struct A {
void operator()() {
std::cout << "This is A Object" << std::endl;
}
};
...
int main(...){
...
A a;
func = a;
func();
...
}

模板的完全特例化:所有参数都标记上

模板的部分特例化:例如只表明是指针,没表明是哪种类型的指针

bind绑定器,绑定参数

void hello ( string str) { cout << str << endl; }
int sum ( int a, int b){ return a + b; }
bind (hello, "hello bind ! ")();
cout <<bind (sum, 10,20)() <<endl;
//参数占位符绑定器出了语句,无法继续使用
bind (hello,_1) ( "hello bind 2 ! " );
cout << bind (sum,_1,_2) (200,300 )<<endl;
//此处把bind返回的绑定器binder就复用起来了
function<void(string) > func1 = bind (hello,_1);
func1 ( "hello china ! " ) ;
func1 ( "hello shan xi ! " );
func1 ( "hello si chuan ! " );

lambda表达式

lambada表达式的语法

//[捕获外部变量](形参列表)->返回值{操作代码};
auto func1 = []()->void {cout << "hello world!" << endl;};

如果不需要返回值那么->void可以省略

->可以省略(1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定; (2):如果function body中没有return语句,则返回值为void类型。

捕获外部变量

[]不捕获任何外部变量

[=]传值捕获

[&]传引用捕获

[this]捕获外部的this指针

[=,&a]引用捕获a其余传值捕获

int a = 10;
int b = 20;
auto func3 = [&a,&b]()
{ int tmp = a;
a = b
b = tmp;
};
func3(); //a=20 b=10

lambada表达式->函数对象function

map<int,function<int(int,int)>> caculateMap;
caculateMap[1] = [](int a, int b)->int{return a + b;};
caculateMap[2] = [](int a, int b)->int{return a - b;};
caculateMap[3] = [](int a, int b)->int{return a * b;};
caculateMap[4] = [](int a, int b)->int{return a / b;};

修改捕获变量

前在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。那么有没有办法可以修改值捕获的外部变量呢?这是就需要使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量,示例:

int a = 123;
auto f = [a]()mutable { cout << ++a; }; // 不会报错 输出124
posted @   霁空  阅读(132)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示