概述
代码架构中一种常见的组织结构 class A --> class B,A作为上层类型,可以调用类型B的方法。然而B类型也有可能会回调A类型的方法(同时修改A成员变量的值)。实现时可能有多种方案(假设都是单例模式)
- B类型中也包含A类型指针作为成员变量,这种方式实现最为简单,但要求A类型对于B类型是完全可见的(紧耦合);
- 使用lambda表达式,在A类型调用B类型方法时,指定回调函数,这样B类型就不必去感知A类型的存在(松耦合);
代码1
#include <iostream>
#include <functional>
using namespace std;
class B{ // 下层类型,可以不知道A类型的存在
public:
void set_func(function<void(int)> func) { f_callable = func; } // 设置回调class A的函数
void call_func(int c) { f_callable(c); } // 执行回调,修改A类型成员变量的值
private:
function<void(int)> f_callable;
};
class A{ // 上层类型
public:
void set_value(int n) { i_a = n; }
void set_function() {
m_b.set_func([&](int m) { this->set_value(m); } ); // lambda表达式设置class B的回调函数,class B使用该函数可以修改A类型成员变量的值
}
int get_value() { return i_a; }
B m_b;
private:
int i_a;
};
int main() {
A a;
a.set_value(1);
cout << a.get_value() << endl; // 输出1
a.set_function();
a.m_b.call_func(2);
cout << a.get_value() << endl; // 输出2
return 0;
}
传递函数对象时,其实隐氏包含了指向A类型的指针,这样B类型对象才可以修改A类型对象成员变量的值。使用这种方式一定要注意类型的生命周期,避免使用B类型的回调函数时A类型已经被析构了。
代码2
代码1中显氏的使用了函数对象作为B类型的成员变量,在某些场景下我们还可以进一步简化。
#include <iostream>
#include <functional>
using namespace std;
class B{ // 下层类型,可以不知道A类型的存在
public:
void method(int n, function<void(int)> func) { func(n); } // 执行回调,修改A类型成员变量的值
};
class A{ // 上层类型
public:
void set_value(int n) { i_a = n; }
void call_B(int m) { // 调用class B的函数,同时设置回调函数
m_b.method(m, [&](int m) { this->set_value(m); } ); // lambda表达式设置class B的回调函数,class B使用该函数可以修改A类型成员变量的值
}
int get_value() { return i_a; }
private:
B m_b;
int i_a;
};
int main() {
A a;
a.set_value(1);
cout << a.get_value() << endl; // 输出1
a.call_B(2);
cout << a.get_value() << endl; // 输出2
return 0;
}
小结
在某些场景使用lambda表达式可以实现上下层类型的解耦,减少类型中定义的函数个数,同时代码更加整洁和优雅。