首页 > 编程语言 >C++智能指针、绑定器和函数对象、lambda表达式

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

时间:2023-03-28 14:55:06浏览次数:48  
标签:int 绑定 C++ 计数 对象 引用 lambda ptr 指针

智能指针

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

不带引用计数的智能指针

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

标签:int,绑定,C++,计数,对象,引用,lambda,ptr,指针
From: https://www.cnblogs.com/KongJiBlogs/p/17265137.html

相关文章

  • C++开发方向书籍推荐
    如果你正在学习C++,那么一本好的教材或参考书可以事半功倍。以下是几本我个人推荐的C++书籍或视频:C++基础看书C++PrimerC++程序设计语言EffectiveC++MoreEffectiv......
  • vue3 中 pinia 的 state 修改模版绑定的 state 数据没更新?
    解决方案:给state增加 computedimport{useLayerStore}from"@/stores/";constlayer=useLayerStore();constlayerList=computed(()=>layer.layerList);......
  • IMU和GPS ekf融合定位 从matlab到c++代码实现 基于位姿状态方程,松耦合
    IMU和GPS ekf融合定位从matlab到c++代码实现基于位姿状态方程,松耦合文档原创且详细YID:6745659043907933......
  • C++11之智能指针shared_ptr
    在C++开发中,我们经常会遇到程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当造成的。C++11新标准中,增添了uni......
  • C++ primer第十五章总结
    1oop的思想是数据抽象继承和动态绑定数据抽象可以将类的接口与实现分离;继承动态绑定,又称运行时绑定2虚函数是基类希望其派生类进行覆盖的函数<1>任何构造函......
  • C++学习路线
    C++是一种高级编程语言,广泛用于开发操作系统、应用程序、游戏和各种工具。如果你想学习这门语言,以下是一个适合初学者的学习路线:第一步:学习C++基础知识在学习C++之前,你需......
  • lambda表达式
    作用:简化函数式接口的匿名内部类写法使用前提:必须是接口的匿名内部类,接口中只能有一个抽象方法好处:lambda是一个匿名函数,我们可以把lambda表达式理解为是一段可传递的代......
  • C++ stringstream ssin 的用法
    C++中stringstream方法存在于头文件<sstream>中作用:使用stringstream方法,将某一字符串生成输入流,然后可以利用这个输入流把长的整行字符串转换成单个字符#include......
  • C++ STL标准库 迭代器相关
    迭代器是什么及用法详解[迭代器是什么及用法详解C语言中文网](http://c.biancheng.net/view/6675.html)迭代器是C++STL(标准模板库)中一种非常重要的概念,它......
  • Vue核心 模板语法 数据绑定
    1.3.模板语法Vue模板语法包括两大类1插值语法功能:用于解析标签体内容写法:{{xxx}},xxx是js表达式,可以直接读取到data中的所有区域2指令语法功能:用于解析标签(包括:标签属......