[学习][记录] c++语言:从放弃到入门 <一> c++11新关键字以及引入的新特性(下)

简介: [学习][记录] c++语言:从放弃到入门 <一> c++11新关键字以及引入的新特性(下)

二十一、 std::bind

21.1 含义

bind 用来将可调用对象和参数一起进行绑定。可调用对象包括普通函数、全局函

数、静态函数、类静态函数甚至是类成员函数,参数包括普通参数和类成员。

语义

std::bind(funcName,argType,…);//绑定以存在的函数名,以及传入的实参

placeholders::_x 实参占位

placeholders::_1 表示第一个实参暂时不填实参数值,依次可推。

21.2 作用

21.2.1 绑定普通函数与参数及占位

double myDivide (double x, double y);
auto fn_five = std::bind (myDivide, 10, 2); 
cout << fn_five() << endl;

21.2.2 绑定对象与成员及占位

class MyPair2 
{
public: 
  int add(int x, int y) 
  { 
return x + y; 
  } 
};
auto bindObjfunc = bind(&MyPair2::add,mp2 , 2, 3); 

21.2.3 函数重载情形

int add(int x, int y);
double add(double x, double y);
auto bindGlobalFunc = 
bind((int(*)(int,int))add,_1,_2);
auto bindGlobalFunc2 = 
bind(static_cast<double(*)(double,double)>(add),_1,_2);

注意

1.预先绑定的参数值通过 值传递进去;通过placehodler传递进去的 传递引用进去

2.bind 返回的是可调用的函数

3.绑定的参数 调用之前 需要确认是可用的

21.3 多态之 bind +fucntion

21.3.1.可实现多态使用

std::function<void(void)> f;
void foo();
void func(int a)
f = std::bind(foo); 
f(); 
f= std::bind(func,1); 
f(); 

21.3.2.function 本是不可以包装类成员函数,bind实现类成员函数的绑定, 然后赋给 fucntion 对象,亦即实现了间接性的包装

std::function<void(void)> f;
class Foo 
{
public: 
void method() 
{ 
cout<<"Foo::void method()"<<endl; 
}
void method2(string s) 
{ 
cout<<"Foo:void method2()"<<endl; 
} 
};
Foo foo; 
f = std::bind(&Foo::method,&foo); 
f(); 
f = std::bind(&Foo::method2,&foo,"china"); 
f();

二十二、 Unordered Contrainer 无序容器

22.1 unordered_map

采用hash_map 数据结构,无序

二十三、 Auto Memeory Manage 自动内存管理

23.1 RAII(Resource Acquisition Is Initialization)

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制

程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之

在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际 上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

①不需要显式地释放资源。 ②采用这种方式,对象所需的资源在其生命期内始终保持有效。

此时,所托管的资源,随对象的创建而获取,随对象的消失而消失,即所谓的 RAII 思想:资源获取即初始化。

原理上,是代理了被托管的对象指针,管理对象的生命周期,即实现自动释放。其 行为类似于所托管的对象指针,原因是,重载了 operator->和 operator*。 如下是智能指针的模板(template)化实现。

template <class T> class SmartPtr {
public:
  explicit SmartPtr(T* pointee) : pointee_(pointee){}
   ~SmartPtr() {
    delete pointee_;
   }
  T& operator*() const { //... 
  return *pointee_;
  }
  T* operator->() const { //... 
  return pointee_; 
  } 
private:
   T* pointee_; //... 
};

23.2 auto_ptr(deprecated)

代理new出来的对象,自动化释放对象,作用域处结束,释放对象。

存在问题

  • 两个auto_ptr对象拥有同一个对象时,析构时都试图删除p,会出现崩溃问题
#include <iostream> 
 #include <memory> 
 using namespace std; 
 int main() {
  int *p = new int(10); 
  {
   auto_ptr<int> ap(p);
   cout<<*ap<<endl;
  }
  auto_ptr<int> ap2(p);
  cout<<*ap2<<endl;
 return 0; 
}
  • 作参数传递的时,亦是会出现同样的情况,两个指针,对同一段资源产生了引用行为。

23.3 unique_ptr

uniqu_ptr 的使用方法,基本上等同于 auto_ptr, 不同之处,就在于实现了资源的 唯一, 既不可以拷贝也不可以赋值,正如其名字一样。

  • 解决了作为 参数拷贝时,无法拷贝和赋值的特性使 只能使用同一个对象,解决auto_ptr造成的释放崩溃
  • 判断是否有资源
unique_ptr<Copy> up; 
if(!up)
{
  cout<<"无资源托管"<<endl; 
}
unique_ptr<Copy> up2(new Copy(10)); 
if(up2)
{
 cout<<"有资源托管"<<endl;
 }
  • 允许托管左值 unique_ptr up2 =std::move(up); 相当于资源转移,被转移的资源不允许再使用
int main() { 
  unique_ptr<Copy> up(new Copy(99));
  cout<<up.get()<<endl;
  unique_ptr<Copy> up2 =std::move(up);
  cout<<up2.get()<<endl;
  //up.get();//被转移资源 再使用 会造成崩溃 
}

23.3.1 常用函数

  • get() 返回所托管资源的指针
  • release() 取消代理(放弃托管) ,返回资源句柄,注意:对象释放仍需手动维护
  • reset() 重置 1.参数为空 释放之前的资源对象 2.参数为对象指针,先释放之前的资源对象,重新托管新的对象
  • std::move() 允许托管左值 相当于资源转移,被转移的资源不允许再使用

23.4 shared_ptr

shared_ptr之间共享资源,共享同一个计数器,每次引用,引用计数+1

int main() { 
  shared_ptr<int> sp(new int(10)); 
  cout<<sp.get()<<endl;
  if(sp) {
  cout<<"有资源托管中"<<endl;
  } 
  cout<<sp.use_count()<<endl;
   {
    shared_ptr<int> sp2 = sp; 
    cout<<sp2.use_count()<<endl; 
  }
  cout<<sp.use_count()<<endl;
  return 0; 
}
输出结果:
有资源托管中

比较:

  • uinque_ptr 解决了 auto_ptr 中引用同一个对象的问题,方式就是不可引用同一个 对象。
  • shared_ptr 解决了 auto_ptr 中引用同一个对象,共同拥有一个资源, 但不会重 析构的问题,原理是,在内部保持一个引用计数,并且仅当引用计数为 0 才能被删除, 不可以用数组。

都无法避免作用域重复析构崩溃

23.5 用法

  • 基本类型计数测试
#include <iostream>
 #include <memory> 
 using namespace std; 
 void func(shared_ptr<int> sp) {
 // sp.reset(); //此处仅将自己所累积的计数减 1 
 cout<<sp.use_count()<<endl; sp.reset(); //此时 reset 等价于 sp 对象消失,若己为零,则不再减 1. 
 }
int main() { 
shared_ptr<int> sp(new int(10)); 
cout<<sp.get()<<endl;
if(sp){
  cout<<"有资源托管中"<<endl;
}
cout<<sp.use_count()<<endl; 
shared_ptr<int> sp2 = sp; 
cout<<sp2.use_count()<<endl; 
cout<<sp.use_count()<<endl; 
func(sp);
cout<<sp.use_count()<<endl; 
return 0; 
}
  • 对象计数测试

reset 跟参数,会托管新对象,释放旧对象,如若不跟参数话,会将当前对象的引 用计数减 1

#include <iostream> 
#include <memory> 
using namespace std; 
class A {
public: A() { 
  cout<<"A()"<<this<<endl; }
~A() { 
  cout<<"~A()"<<this<<endl; 
}
void dis() { 
  cout<<"A::void dis()"<<endl; 
} 
};
int main1() { 
{ 
 shared_ptr<A> sp(new A);
 sp.reset(new A()); 
 cout<<"+++++++++++++++++++++"<<endl; 
}
cout<<"======================"<<endl;
}
int main() 
{ 
  { 
    shared_ptr<A> sp(new A); 
    sp.reset();sp.reset();sp.reset(); 
    cout<<"+++++++++++++++++++++"<<endl; 
  }
  cout<<"======================"<<endl; 
}
  • 对象传参测试
#include <iostream> 
#include <memory> 
using namespace std; 
class A {public: A() {
 cout<<"A()"<<this<<endl; 
}
~A() { 
cout<<"~A()"<<this<<endl; 
}
void dis() { 
 cout<<"A::void dis()"<<endl; 
} 
};
void func(shared_ptr<A> &sp) //shared_ptr<A> &sp 
{ 
  cout<<sp.use_count()<<endl; 
  sp.reset(); sp.reset(); 
  cout<<"======================"<<endl; 
}
int main() 
{ 
  shared_ptr<A> sp(new A);
  cout<<sp.use_count()<<endl; 
  func(sp); 
  cout<<sp.use_count()<<endl; 
  shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走 
  cout<<sp2.use_count()<<endl; 
  cout<<sp.use_count()<<endl; //此时资源为 0 
  return 0; 
}

对同一个对象,多次 reset 的结果,是仅对自己增加的计数减 1。要保证,当前的 对象的使用安全性。也不会对其它象的使用造成影响。

void func(shared_ptr<Copy> spc) //&spc 若传递的是引用,则引用计数不会加 1。离开函数也不会减 1 
shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走

23.5.1 常用函数

  • reset() 只可自杀 不可他杀
  • std::move() 计数不会增加
  • operator bool 判空
  • operator= 支持赋值,复制

23.6 weak_ptr

#include <memory>
std::weak_ptr<int> wp1;

只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具。

借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息:

  • 有多少指向相同的 shared_ptr 指针、
  • shared_ptr 指针指向的堆内存是否已经被释放等等。

注意:

当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;

同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。

也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数。

相关文章
|
5天前
|
编译器 C++ 开发者
C++一分钟之-C++20新特性:模块化编程
【6月更文挑战第27天】C++20引入模块化编程,缓解`#include`带来的编译时间长和头文件管理难题。模块由接口(`.cppm`)和实现(`.cpp`)组成,使用`import`导入。常见问题包括兼容性、设计不当、暴露私有细节和编译器支持。避免这些问题需分阶段迁移、合理设计、明确接口和关注编译器更新。示例展示了模块定义和使用,提升代码组织和维护性。随着编译器支持加强,模块化将成为C++标准的关键特性。
21 3
|
6天前
|
存储 安全 编译器
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
33 5
|
7天前
|
安全 编译器 C语言
【C++】学习笔记——C++入门_3
【C++】学习笔记——C++入门_3
20 4
|
6天前
|
安全 编译器 程序员
【C++初阶】--- C++入门(上)
【C++初阶】--- C++入门(上)
12 1
|
6天前
|
存储 安全 编译器
【C++初阶】--- C++入门(下)
【C++初阶】--- C++入门(下)
8 0
|
6天前
|
存储 编译器 Linux
【C++初阶】--- C++入门(中)
【C++初阶】--- C++入门(中)
11 0
|
6天前
|
Unix 编译器 C语言
【C++航海王:追寻罗杰的编程之路】关键字、命名空间、输入输出、缺省、重载汇总
【C++航海王:追寻罗杰的编程之路】关键字、命名空间、输入输出、缺省、重载汇总
8 0
|
4天前
|
C++
【C++】日期类Date(详解)②
- `-=`通过复用`+=`实现,`Date operator-(int day)`则通过创建副本并调用`-=`。 - 前置`++`和后置`++`同样使用重载,类似地,前置`--`和后置`--`也复用了`+=`和`-=1`。 - 比较运算符重载如`&gt;`, `==`, `&lt;`, `&lt;=`, `!=`,通常只需实现两个,其他可通过复合逻辑得出。 - `Date`减`Date`返回天数,通过迭代较小日期直到与较大日期相等,记录步数和符号。 ``` 这是236个字符的摘要,符合240字符以内的要求,涵盖了日期类中运算符重载的主要实现。
|
6天前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
10 0
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
3天前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。