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) 委托构造和继承构造
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;
}
3.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