C++ 头文件的循环引用
前言
在实现设计模式中的观察者模式时,想要完成的功能是:被观察者数据改变时通知观察者,而观察者能够知悉是哪些数据发生了改变(被观察者作为参数传递)
这里就产生了循环引用,被观察者中包含了观察者,而观察者也包含了被观察者,导致出错。
解决方案:前向声明
在形如以下的循环引用结构,可以通过前向声明解决
// A.h
#include "B.h"
class B;
class A{
public:
B* member1;
B& member2;
void func(B*);
void func(B&);
};
// A.cpp
...
// B.h
#include "A.h"
class A;
class B{
public:
A* member1;
A& member2;
void func(A&);
void func(A*);
};
// B.cpp
...
要注意,类 A
包含类 B
或类 B
包含类 A
只能以指针、引用、或者用于函数形参的指针和引用的形式,且其实现必须在源文件中实现,而无法在头文件中。
这是因为编译器构造类时必须知道所占用的内存大小,因为存在循环引用,在构造一个类时,无法知道另一个类的大小,所以此时只能用指针或引用(此时内存大小是可以确定的)
此时如果不采用前向声明,则会报错:
'x' has not been declared
'x' does not name a type
解决方案:解除循环引用
在上述的循环引用结构中,A
和 B
互相引用,是一种高度耦合的状态,此时出现一个第三者,则可以打破这种循环结构,也对二者进行解耦。
A
和 B
的相互引用,无非是方便获取数据或调用某些操作,可以通过第三者接口来进行循环的破坏。
通过接口 C
解耦 A
和 B
,现在 B
无需知道 A
的具体细节,只需要调用 C
提供的接口即可。
此时代码:数据以 int
为例
// A.h
#include "C.h"
class A{
public:
void func(){
std::cout << member->getData() << std::endl;
}
C* member;
};
// B.h
#include "C.h"
#include "A.h"
class B : public C{
public:
int getData() override {
return data;
}
void func(A& a){
a.func();
}
int data;
};
// C.h
class C{
public:
virtual int getData() = 0;
};
解决方案:将数据作为参数
如上所述,循环引用无非是获取某些数据或调用某些函数,此时可以将一方的引用去除,而转为接受参数。但是这种方案割舍了某些功能(只能单向通知),但是作为观察者模式的实现,可以比较好的完成。
被观察者数据改变时通知观察者,而观察者能够知悉是哪些数据发生了改变,这些数据通过参数传递。
简化后的结构:
// Subject.h
class Subject{
public:
void setData(int data){
data_ = data;
notifyObservers();
}
...
private:
void notifyObservers(){
obs_->update(data_);
}
Observer* obs_;
int data_;
};
// Observer.h
class Observer{
public:
void update(int data){
std::cout << "监听到数据改变: " << data << std::endl;
}
};
评论 |
0条评论