写在前面
本文为了优雅的解决类之间函数回调问题,由于第一次发表原创文章,故存在披露在所难免,有问题大家友好讨论。
开门见山
先上代码,再介绍代码里面的一些知识点及关键点。代码均可完美编译运行,编译运行环境VS2019
main.cpp
#include <iostream>
#include <functional>//函数指针的头文件
#include <string>
#include "ClassA.h"
#include "ClassB.h"
#include "FunCollectSingleton.h"
using namespace std;
ClassA *pClassA;
ClassB* PClassB;
void BindFunc();
int main(int argc, char* argv[])
{
//这样进行回调 存在风险 就是函数指针未绑定就进行调用 会导致程序崩溃
//未绑定的函数指针相当于野指针/空指针
//所以应该使用接口进行回调可避免这样的问题
BindFunc();
std::cout << "在主函数里面 回调/调用 ClassA::Func1" << endl;
FunCollectSingleton::Instance()->AFunc1();//在主函数里面 回调/调用 ClassA::Func1
std::cout << "在主函数里面 回调/调用 ClassB::Func1" << endl;
FunCollectSingleton::Instance()->BFunc1();//在主函数里面 回调/调用 ClassB::Func1
std::cout << "直接调用" << endl;
pClassA->Func1();//直接调用
std::cout << "在类A中 回调/调用 ClassB::Func1" << endl;
pClassA->CallClassBFunc();//在类A中 回调/调用 ClassB::Func1
std::cout << "" << endl;
std::cout << "FunCollectSingleton::Instance()->AFunc2(10): "<< FunCollectSingleton::Instance()->AFunc2(10) <<std::endl;//回调/调用
return 0;
}
void BindFunc()//绑定
{
pClassA = new ClassA();
pClassA->SetObjName("pClassA");
FunCollectSingleton::Instance()->AFunc1= std::bind(&ClassA::Func1, pClassA);//无参的绑定方式
FunCollectSingleton::Instance()->AFunc2 = std::bind(&ClassA::Func2, pClassA, std::placeholders::_1);//有参的绑定方式
PClassB = new ClassB();
PClassB->SetObjName("PClassB");
FunCollectSingleton::Instance()->BFunc1 = std::bind(&ClassB::Func1, PClassB);//无参的绑定方式
}
FunCollectSingleton.h
#pragma once
#include <functional>
class FunCollectSingleton
{
public:
FunCollectSingleton(FunCollectSingleton&) = delete;
FunCollectSingleton& operator=(const FunCollectSingleton&) = delete;
static FunCollectSingleton* Instance();
public:
std::function<void()> AFunc1;
std::function<int(int)> AFunc2;
//例如:此时要是ClassA中增加一个新方法例如void Func3(int a)
//则对于与这里也要同步增加一个函数指针std::function<void(int)> AFunc3;
//并且绑定的时候依葫芦画瓢即可
std::function<void()> BFunc1;
private:
FunCollectSingleton() {};
static FunCollectSingleton Ins;
};
FunCollectSingleton.cpp
#include "FunCollectSingleton.h"
FunCollectSingleton FunCollectSingleton::Ins;
FunCollectSingleton* FunCollectSingleton::Instance()
{
return &Ins;
}
ClassA.h
#pragma once
#include <string>
class ClassA
{
public:
ClassA();
~ClassA();
public:
void Func1();
int Func2(int a);
//void Func3(int a);
//此时增加一个方法 void Func3(int a);
//对应于FunCollectSingleton 类来说 同步增加一个函数指针std::function<void(int)> AFunc3;即可
//然后绑定调用即可
void SetObjName(std::string objName);
void CallClassBFunc();
private:
std::string ObjName;
};
ClassA.cpp
#include "ClassA.h"
#include <iostream>
#include "FunCollectSingleton.h"
ClassA::ClassA()
{
}
ClassA::~ClassA()
{
}
void ClassA::Func1()
{
std::cout << "ClassA::Func1"<<" ObjName: "<< ObjName << std::endl;
}
int ClassA::Func2(int a)
{
std::cout << "ClassA::Func2" << " ObjName: " << ObjName << std::endl;
return a+1;
}
void ClassA::SetObjName(std::string objName)
{
ObjName = objName;
}
void ClassA::CallClassBFunc()
{
FunCollectSingleton::Instance()->BFunc1();//回调/调用
}
ClassB.h
#pragma once
#include <string>
class ClassB
{
public:
ClassB();
~ClassB();
public:
void Func1();
void SetObjName(std::string objName);
private:
std::string ObjName;
};
ClassB.cpp
#include "ClassB.h"
#include <iostream>
ClassB::ClassB()
{
}
ClassB::~ClassB()
{
}
void ClassB::Func1()
{
std::cout << "ClassB::Func1" << " ObjName: " << ObjName << std::endl;
}
void ClassB::SetObjName(std::string objName)
{
ObjName = objName;
}
介绍
由于代码里面已经有比较完善的注释了,故只介绍代码注释未涉及的部分。
1、
std::function<int(int)> AFunc2;
中的<int(int)>就是函数指针需要变化的部分,例如本例子中<int()>中的int就是需要绑定函数的返回值
而(int)中的int就是需要绑定函数的参数
例如std::function<int(int)> AFunc2;绑定的就是ClassA::Func2即:
FunCollectSingleton::Instance()->AFunc2 = std::bind(&ClassA::Func2, pClassA, std::placeholders::_1);//有参的绑定方式
2、
这里使用单列方式为了保持绑定的函数指针及调用的函数指针是同一份东西,并且方便在任意地方随时访问。
3、倘若有这样的类加载顺序
由于A类的加载链路上完全找不到D类的任何信息,又要回调D类的方法,且不能更改类加载顺序,故通过此方法可以很有效的解决这一问题,而真实的项目远比这简单示意图复杂的多,故如何优雅且安全方便的回调是有必要的。