C++实现Observer设计模式遇未声明标识符等编译错误求助
所有编译错误的核心是头文件循环包含,无其他隐藏问题。
两个头文件互相#include对方,在#pragma once的保护下,会导致编译器加载类型定义时出现“半截加载”的连锁问题:
- 编译
Obs.h时第一行就跳转加载Subject.h - 加载
Subject.h时第一行又跳转加载Obs.h,此时Obs.h因#pragma once被判定为已加载,直接跳过 - 等编译
Subject.h到需要用到Observer类型的位置时,Observer的完整定义根本还没加载到(Obs.h才刚被打开就跳走了) - 切回
Obs.h继续编译时,遇到需要用到Subject类型的位置,Subject的完整定义也没加载到(Subject.h才刚被打开就跳回Obs.h了)
最终就会报出一堆“标识符未声明”“缺少类型说明符”“语法错误缺分号”的连锁报错,和你贴的所有错误信息完全对应。
另外还有两个衍生小问题: - 类的成员函数实现如果要调用对方类的方法,需要拿到对方类的完整定义,不能直接写在头文件里,不然就算拆了循环包含,还是会因为前置声明的类型不完整报错
- 部分头文件里只用到
Subject的指针,之前靠循环传递包含拿到Subject的定义,等循环依赖拆了之后会出现新的未声明错误
1. 重写Obs.h,替换循环包含为前置声明
Observer类的成员、构造函数参数都只用到Subject的指针,不需要拿到Subject的完整定义,直接删掉头文件里的#include "Subject.h",换前置声明;同时把构造函数的实现移到独立的cpp文件里(因为构造函数内部要调用Subject的attach()方法,需要完整定义)
修改后的Obs.h:
#pragma once // 前置声明,替代原来的#include "Subject.h" class Subject; class Observer { Subject* model; public: Observer(Subject* mod); virtual void update() = 0; };
新增Obs.cpp文件,放置Observer构造函数的实现:
#include "Obs.h" #include "Subject.h" Observer::Observer(Subject* mod) { model = mod; model->attach(this); }
2. 重写Subject.h,去掉循环包含
Subject类只存储Observer的指针,不需要Observer的完整定义,直接删掉头文件里的#include "Obs.h",保留vector参数里的内置前置声明;同时把notify()函数的实现移到独立的cpp文件里(因为notify()内部要调用Observer的update()方法,需要完整定义)
修改后的Subject.h:
#pragma once #include <vector> // 删除原来的#include "Obs.h" class Subject { // 这里的class Observer就是内置前置声明,不需要额外包含头文件 std::vector <class Observer*> views; public: void attach(Observer *obs) { views.push_back(obs); } void notify(); };
新增Subject.cpp文件,放置notify函数的实现:
#include "Subject.h" #include "Obs.h" void Subject::notify() { for (int i = 0; i < views.size(); i++) { views[i]->update(); } }
3. 补全其他头文件的前置声明
原来的Class.h、Class1.h里都只用到Subject*类型的指针参数,之前靠Obs.h里的传递包含拿到Subject的定义,现在循环包含拆了之后,要自己加前置声明,避免新的未声明错误。
修改后的Class.h:
#pragma once #include "Obs.h" // 新增前置声明 class Subject; class Class : public Observer { int n1, n2; public: Class(Subject* sub); void setNumbers(int, int); int sum(); void update(); };
对应修改Class.cpp的头文件包含,加上#include "Subject.h",其余类实现保持不变:
#include "Class.h" #include "Subject.h" #include <iostream> // 原有实现代码无需修改
同样修改Class1.h:
#pragma once #include "Obs.h" #include "Class.h" // 新增前置声明 class Subject; class Class1: public Observer { int result; Class c1, c2; public: Class1(Subject* sub); void setNumbers(Class, Class); int multiply(); void update(); };
对应修改Class1.cpp的头文件包含,加上#include "Subject.h",其余类实现保持不变:
#include "Class1.h" #include "Subject.h" #include <iostream> // 原有实现代码无需修改
4. 修正逻辑小问题
原Test.cpp中计算乘积的输出写错了,把直接输出c1.sum()改成调用cl1.multiply()即可:
// 原来的错误代码 // std::cout << "(1 + 3) x (4 + 6) = " << c1.sum() << std::endl; // 修正为 std::cout << "(1 + 3) x (4 + 6) = " << cl1.multiply() << std::endl;
内容的提问来源于stack exchange,提问作者Sıla

