c++学习之十四

2023-09-13 10:11:38

1)利用std::function实现回调函数,实现生产者及消费者模型

// 254、回调函数的实现
// 在消息队列和网络库的框架中,当接收到消息(报文)时,回调用户自定义的函数对象,把消息(报文)参数传给它,由它决定如何处理。
// 示例:

// 254、回调函数的实现
// 在消息队列和网络库的框架中,当接收到消息(报文)时,回调用户自定义的函数对象,把消息(报文)参数传给它,由它决定如何处理。
// 示例:
#include <iostream>
#include <string>
#include <thread>                      // 线程类头文件。
#include <mutex>                      // 互斥锁类的头文件。
#include <deque>                      // deque容器的头文件。
#include <queue>                      // queue容器的头文件。
#include <condition_variable>  // 条件变量的头文件。
#include <functional>
using namespace std;

void show(const string& str)
{
    cout<<"handle dat: "<<str<<endl;
}

struct B
{
    void show(const string& str)
    {
        cout<<"class B handle dat: "<<str<<endl;
    }
};

class A
{
private:
    mutex mtx;
    condition_variable cond;
    queue<string,deque<string>> q;
    function<void(const string&)> callBack;
public:
    // 注册回调函数,回调函数只有一个参数(消费者接收到的数据)。
    template<typename Fn,typename ...Args>
    void callBackFun(Fn&& fn,Args&&... args)
    {
        callBack = bind(forward<Fn>(fn),forward<Args>(args)...,std::placeholders::_1);
    }
    void incache(int num) //生成数据,num指定数据的个数
    {
        lock_guard<mutex> lock(mtx);
        for(int i = 0; i < num; i++)
        {
            static int sn = 1;
            string tmp = to_string(sn++) + "号";
            q.push(tmp); //把生产出来的数据入队列
        }
        // cond.notify_one();
        cond.notify_all(); //唤醒全部条件变量阻塞的线程
    }
    void outcache(void)
    {
        while(1)
        {
            unique_lock<mutex> lock(mtx);
            cond.wait(lock,[this]{return !q.empty();});
            string str = q.front();
            q.pop();
            cout<<"thread id: "<<this_thread::get_id()<<" recv msg "<<str<<endl;
            lock.unlock(); //手工解锁
            callBack(str); //回调函数,处理出队的数据(相当于数据消费掉)
        }
    }
};
int main()
{
    A a;
    a.callBackFun(show);

    B b;
    a.callBackFun(&B::show,&b);
    thread t1(&A::outcache,&a); //创建消费者线程t1;
    thread t2(&A::outcache,&a); //创建消费者线程t2;
    thread t3(&A::outcache,&a); //创建消费者线程t3;

    this_thread::sleep_for(std::chrono::seconds(2));
    a.incache(2); //生产2个数据
    this_thread::sleep_for(std::chrono::seconds(3));
    a.incache(5); //生产5个数据    
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

//输出结果:
thread id: 140711377970944 recv msg 1号
class B handle dat: 1号
thread id: 140711377970944 recv msg 2号
class B handle dat: 2号
thread id: 140711386363648 recv msg 3号
class B handle dat: 3号
thread id: 140711377970944 recv msg 4号
class B handle dat: 4号
thread id: 140711394756352 recv msg 5号
class B handle dat: 5号
thread id: 140711386363648 recv msg 6号
class B handle dat: 6号
thread id: 140711377970944 recv msg 7号
class B handle dat: 7号

2)移动构造函数和移动赋值函数
类名(const 类名&& 源对象){…} //移动构造函数
类名& operator=(const 类名& 源对象){…} //移动赋值函数

#include <iostream>
#include<string.h>

using namespace std;

class A
{
public:
	A()=default;
	int* iPtr = nullptr; //这里nullptr必须写;
	void getMemory()
	{
		if(iPtr == nullptr)
		{
			iPtr = new int;
        	memset(iPtr, 0, sizeof(int));   // 初始化已分配的内存。			
		}
	}

	A(const A& src) //拷贝构造函数
	{
		cout<<"拷贝分配内存"<<endl;		
		if(iPtr == nullptr)
		{
			getMemory();
		}
		*iPtr = *src.iPtr;
	}


	A& operator=(const A& src)
	{
		cout<<"拷贝赋值运算符分配内存"<<endl;		
		if(iPtr == nullptr)
		{
			getMemory();
		}
		*iPtr = *src.iPtr;		
		return *this;
	}

	A(A&& src) //移动构造函数
	{
		cout<<"调用了移动构造函数"<<endl;
		if(iPtr != nullptr) delete iPtr;
		iPtr = src.iPtr; //这句话是最根本的,不能写成 *iPtr = *src.iPtr;
		src.iPtr = nullptr;
	}

	A& operator=(A&& src)
	{
		cout<<"调用了移动赋值运算符函数"<<endl;
		if(iPtr != nullptr) delete iPtr;
		iPtr = src.iPtr; //这句话是最根本的,不能写成 *iPtr = *src.iPtr;
		src.iPtr = nullptr;
		return *this;
	}
};

int main()
{
	A a;
	a.getMemory();
	*a.iPtr = 10;
	cout<<"*iPtr data is: "<<*a.iPtr<<endl;

	A a1(a);
	cout<<"*iPtr1 data is: "<<*a1.iPtr<<endl; //调用拷贝构造函数

	A a3;
	a3 = a;
	cout<<"*iPtr3 data is: "<<*a3.iPtr<<endl; //调用拷贝赋值运算符成员函数

	auto f = [] { A aa; aa.getMemory(); *aa.iPtr = 8; return aa; }; 
	A a4 = f();
	cout<<"*iPtr4 data is: "<<*a4.iPtr<<endl; //调用了移动构造函数

	A a5;
	a5 = f();
	cout<<"*iPtr5 data is: "<<*a5.iPtr<<endl; //调用了移动构造函数	
	return 0;
}

//输出结果如下:
*iPtr data is: 10
拷贝分配内存
*iPtr1 data is: 10
拷贝赋值运算符分配内存
*iPtr3 data is: 10
调用了移动构造函数
调用了移动构造函数
*iPtr4 data is: 8
调用了移动构造函数
调用了移动赋值运算符函数
*iPtr5 data is: 8

3) 委托构造和继承构造

.1)委托构造,【委托构造必须在同一个类里】;
#include <iostream>
using namespace std;
//一旦使用委托构造,就不能在初始化列表中初始化其它的成员变量
class A
{
public:
	A(int a):a_(a){cout<<"a constructor----"<<endl;}
	A(int a,string b):A(a) //A(int a,int b):A(a),b_(b)是错误的,//委托构造不能有其他的成员初始化列表
	{ 
		this->str_ = b;
		cout<<"委托构造 "<<b<<endl;
	}
private:
	int a_;
	string str_;
};

int main(void)
{
	A a1(1); //没有调用委托构造函数
	A a2(11,"hello world"); //调用了委托构造函数
	return 0;
}.2)继承构造
#include <iostream>
using namespace std;

class AA       // 基类。
{
public:
    int      m_a;
    int      m_b;
    // 有一个参数的构造函数,初始化m_a
    AA(int a) : m_a(a) { cout << "base  AA(int a)" << endl; }
    // 有两个参数的构造函数,初始化m_a和m_b
    AA(int a, int b) : m_a(a), m_b(b) { cout << "base   AA(int a, int b)" << endl; }
};

class BB :public AA       // 派生类。
{
public:
    double   m_c;
    using AA::AA;     // 使用基类的构造函数。构造函数继承!!!
    // 有三个参数的构造函数,调用A(a,b)初始化m_a和m_b,同时初始化m_c
    BB(int a, int b, double c) : AA(a, b), m_c(c) {
        cout << " derived BB(int a, int b, double c)" << endl;
    }
    void show() { cout << "m_a=" << m_a << ",m_b=" << m_b << ",m_c=" << m_c << endl; }
};

int main()
{
    // 将使用基类有一个参数的构造函数,初始化m_a
    BB b1(10);       
    b1.show();

    // 将使用基类有两个参数的构造函数,初始化m_a和m_b
    BB b2(10,20);  
    b2.show();

    // 将使用派生类自己有三个参数的构造函数,调用A(a,b)初始化m_a和m_b,同时初始化m_c
    BB b3(10,20,10.58);  
    b3.show();
}

const关键字从功能上来说有双重语义:只读变量和修饰常量。
using 和 typedef都可以设置模板别名,但using可以用于部分模板具体化;
传统方法用sprintf()和snprintf()函数把数值转换为char字符串;用atoi()、atol()、atof()把char字符串转换为数值。
C++11提供了新的方法,在数值类型和string字符串之间转换。“to_string()”
默认情况下,由lambda函数生成的类是const成员函数,所以变量的值不能修改。如果加上mutable,相当于去掉const。这样上面的限制就能讲通了
4)字符串转整形的函数“stoi”的使用方法:

#include <iostream>
#include<string>
#include<string.h>

using namespace std;
int main()
{
	string str("1q2A345");
	size_t pos = 0; //pos,是传出参数,存放从哪个字符开始无法继续解析的位置
	int val = stoi(str, &pos, 10);
	cout<<pos<<endl;  //1
	cout<<val<<endl; //1
	return 0;
}

5)异常处理的案例

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
	try
	{
		int ii;
		cin>>ii;
		if(ii == 1)
		{
			cout<<"111111111111"<<endl;
			throw(12);
		}
		if(ii == 2)
		{
			cout<<"222222"<<endl;
			throw("const char* number");			//抛出的是const char*
		}		
		if(ii == 3)
		{
			cout<<"3333"<<endl;
			throw string("string number");			//抛出的是string
		}		
	}
	catch(int tmp)
	{
		cout<<"int type: tmp "<<tmp<<endl;
	}
	
	catch(const char* str) //const char*
	{
		cout<<"string type: tmp "<<str<<endl;
	}	

	catch(string str) 
	{
		cout<<"string type: tmp "<<str<<endl;
	}		
	return 0;
}

6)读文件时,badbid,failbit,goodbit,eofbit的flag的变化及解释:
//测试文件名字:123.txt,其内容是"aaa";

#include <iostream>
#include <fstream>  // ifstream类需要包含的头文件。
#include <string>     // getline()函数需要包含的头文件。
using  namespace std;

int main()
{
	ifstream fin("123.txt", ios::in);

	if (fin.is_open() == false)
	{
		cout << "打开文件" << R"(D:\data\txt\test.txt)" << "失败。\n";  return 0;
	}
	// fin.good();//正常读取文件时返回true
	// fin.fail();/在文件到达结尾或者出现其他输入错误如内存不足时返回true
	// fin.eof();//当文件到达结尾的返回true。
	string buffer;
	while (true)
	{
		fin >> buffer;
		cout << "eof()=" << fin.eof() << ",good() = " << fin.good() << ", bad() = " << fin.bad() << ", fail() = " << fin.fail() << endl;
		if (fin.eof() == true) break;

		cout << buffer << endl;
	}

	fin.close();	   // 关闭文件,fin对象失效前会自动调用close()。
}

//输出结果是:
eof()=0,good() = 1, bad() = 0, fail() = 0
aaa
eof()=1,good() = 0, bad() = 0, fail() = 1

更多推荐

SpringBoot中级开发--事务配置管理(10)

事务在整个开发框架中是一个非常常用的功能,特别涉及到数据库操作。像Mysql,就有4个数据库级别:(1)READUNCOMMITTED(读未提交):允许读取未提交的数据。这种级别的事务可以读取到其他事务未提交的数据,可能会导致脏读、不可重复读和幻读等问题。(2)READCOMMITTED(读已提交):只能读取已经提交的

接口测试工具详解

首先,什么是接口呢?接口一般来说有两种,一种是程序内部的接口,一种是系统对外的接口。系统对外的接口:比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用他提供的接口就能使用他写好的方法,从而达到数据共享的目的,比如说咱们用的app、网址这些它在进行

c#设计模式-创建型模式 之 建造者模式

简介:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。提供了一种创建对象的最佳方式。一个Builder类会一步一步构造最终的对象。该Builder类是独立于其他对象的。意图是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。建造者模式的核心思想就是将一个复杂对象的构建与其表示分

二,手机硬件参数介绍和校验算法

系列文章目录第一章安卓aosp源码编译环境搭建第二章手机硬件参数介绍和校验算法第三章修改安卓aosp代码更改硬件参数第四章编译定制rom并刷机实现硬改(一)第五章编译定制rom并刷机实现硬改(二)第六章不root不magisk不xposedlsposedfrida原生修改定位第七章安卓手机环境检测软件分享第八章硬改之设

【C语言】指针的进阶(一)

目录前言1.字符指针2.指针数组3.数组指针3.1数组指针的定义3.2&数组名VS数组名3.3数组指针的使用4.数组参数、指针参数4.1一维数组传参4.2二维数组传参4.3一级指针传参4.4二级指针传参5.函数指针前言指针在C语言中可谓是有着举足轻重的存在,初学C语言的我们在《指针》章节已经接触过了一些指针的知识,知道

【网络豆送书第四期】《用户画像:平台构建与业务实践》

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。公众号:网络豆座右铭:低头赶路,敬事如仪个人主页:网络豆的主页​​​​​本期好书推荐:《用户画像:平台构建与业务实践》粉丝福利:书籍赠送:共计送出4本参与方式:关注公众号:网络豆云计算学堂回复关键词:第四期送书截止时间:2023年9月24日中午12:00

嵌入式C语言知识复习和提高

文章目录前言基础知识main函数防BUG注释(重要)关键字标识符命名(驼峰命名)常量类型变量printf1.输出不同类型数据2.输出不同宽度数据3.不同类型数据长度归类scanf函数运算符sizeof(运算符,优先级2)逗号运算符关系运算符逻辑运算符三目运算符强制类型转换流程控制if语句switchcase循环结构fo

Python基于Flask的高校舆情分析,舆情监控可视化系统

目录一、数据采集二、数据预处理三、舆情分析四、数据可视化五、系统集成六、用户交互功能实现:七、系统优化总结随着互联网的普及和信息量的爆炸式增长,网络舆情数据变得越来越庞大和复杂。高校作为社会的重要组成部分,其舆情数据同样具有重要意义。因此,为了更好地监控和了解高校舆情数据的动态和发展趋势,我们需要构建一个基于Flask

Linux 终端命令总结

一、常用的七条命令命令对应英文作用lslist查看当前文件夹下的内容pwdprintworkdirectory查看当前所在文件夹cd[目录名]changedirectory切换文件夹touch[文件名]touch如果文件不存在新建文件mkdir[目录名]makedirectory创建目录rm[文件名]remove删除指

Appium元素定位

1.目的当今社会,人们的生活几乎已经无法离开形形色色的APP了,它提供给我们的便利与服务意义远远超出了其本身的软件价值。作为测试来说移动应用也早已是各大互联网公司的拳头产品,其本身的开发周期短,附属产品价值高等特性决定了今后的主导地位。那么在日常的测试活动中,移动应用的质量保障就成为了各个测试团队的主要课题,面对高速迭

软件定制app开发方式与优势|小程序搭建

软件定制app开发方式与优势|小程序搭建目前软件定制app开发方式主要两种1、利用模板开发很多软件开发公司会以模板的方式出售,即现有的app功能和设计样式都是现成的,无需从零开始定制开发。优点:费用低,一般价格在几千到上万。快速上线,方便实惠;缺点:功能固定,难以修改。在此之前需要确定自己需要哪些功能,现有app模板是

热文推荐