c++学习之十四

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

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

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

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 
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 
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 
#include
#include

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 
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 
#include   // ifstream类需要包含的头文件。
#include      // 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

你可能感兴趣的:(c++,学习,开发语言)