实现Visitor设计模式时遭遇编译错误,寻求技术帮助
解决Visitor模式实现中的编译错误与逻辑问题
我来帮你逐一排查并解决这些问题:
问题1:前向声明导致的未定义类型错误
你的代码中,Forma类的accept函数在Operatie类完全定义之前就尝试调用op->vizitare(this),但此时编译器只知道Operatie是一个类的前向声明,不知道它的成员函数vizitare的具体结构,因此无法解析这个调用。
修正方法:
将Forma类的accept设为纯虚函数(符合抽象基类的设计,强制子类必须实现该方法),同时调整类的声明顺序,确保Operatie类在子类实现accept之前完成定义:
class Forma { public: // 纯虚函数,子类必须实现 virtual void accept(Operatie * op) = 0; Forma(void){}; virtual ~Forma(void){}; // 添加虚析构,确保子类析构被正确调用 };
问题2:悬挂指针引发的内存安全问题
Editor::insert函数接收Forma对象的值,然后把局部对象的地址存入链表。当insert函数执行完毕后,这个局部对象会被销毁,链表中的指针就变成了悬挂指针,后续访问会导致未定义行为(比如程序崩溃、输出乱码)。
修正方法:
修改insert函数接收Forma*类型的参数,让调用者传入动态分配的对象指针,同时在Editor的析构函数中释放这些对象避免内存泄漏:
// Editor类的insert方法修改 void insert(Forma* obj){forme.insert(forme.end(), obj);}; // Editor的析构函数添加内存释放逻辑 ~Editor(void){ for(auto it = forme.begin(); it != forme.end(); ++it){ delete *it; } };
问题3:缺少必要头文件与命名空间问题
main函数中使用了cout和endl,但没有包含<iostream>头文件,而且这些属于std命名空间,必须显式指定才能使用。
修正方法:
添加头文件并使用标准命名空间:
#include <iostream> // 在main中使用std::前缀,或者全局声明(不推荐全局,但可简化代码) std::cout << editor.calculeazaArieTotala() << std::endl;
问题4:计算逻辑与拼写错误
你写的面积、周长公式完全错误,还有变量名拼写失误:
- 圆的面积应为
M_PI * r * r(你写成了M_PI * r) - 矩形的面积应为
长 × 宽(你写成了长 + 宽) - 圆的周长应为
2 * M_PI * r(你写成了面积公式M_PI * r * r) Perimetru类的实例名拼写错误:permietru→perimetru
修正方法:
修改Arie和Perimetru类的计算逻辑:
// Arie类的vizitare方法修正 void vizitare(Cerc* obj) override {arie += M_PI * obj->getRaza() * obj->getRaza();}; void vizitare(Dreptunghi* obj) override {arie += obj->getLungime() * obj->getLatime();}; // Perimetru类的vizitare方法修正 void vizitare(Cerc* obj) override {perimetru += 2 * M_PI * obj->getRaza();}; void vizitare(Dreptunghi* obj) override {perimetru += 2*(obj->getLungime() + obj->getLatime());}; // main中的变量名修正 Perimetru perimetru;
完整修正后的代码
#define _USE_MATH_DEFINES #include <list> #include <iostream> #include "math.h" // 前向声明 class Operatie; class Cerc; class Dreptunghi; class Forma { public: virtual void accept(Operatie * op) = 0; Forma(void){}; virtual ~Forma(void){}; }; class Operatie{ public: virtual void vizitare(Cerc* obj) = 0; virtual void vizitare(Dreptunghi* obj) = 0; Operatie(void){}; virtual ~Operatie(void){}; }; class Cerc : public Forma { protected: int raza; public: void accept(Operatie * op) override {op->vizitare(this);}; int getRaza(){return raza;}; Cerc(void):raza(3){}; ~Cerc(void){}; }; class Dreptunghi : public Forma { protected: int lungime; int latime; public: void accept(Operatie * op) override {op->vizitare(this);}; int getLungime(void){return lungime;}; int getLatime(void){return latime;}; Dreptunghi(void):lungime(3),latime(3){}; ~Dreptunghi(void){}; }; class Arie : public Operatie { private: double arie; public: double getResult(){return arie;}; void vizitare(Cerc* obj) override {arie += M_PI * obj->getRaza() * obj->getRaza();}; void vizitare(Dreptunghi* obj) override {arie += obj->getLungime() * obj->getLatime();}; Arie(void): arie(0) {}; ~Arie(void){}; }; class Perimetru : public Operatie { private: double perimetru; public: double getResult(){return perimetru;}; void vizitare(Cerc* obj) override {perimetru += 2 * M_PI * obj->getRaza();}; void vizitare(Dreptunghi* obj) override {perimetru += 2*(obj->getLungime() + obj->getLatime());}; Perimetru(void):perimetru(0){}; ~Perimetru(void){}; }; class Editor{ private: std::list<Forma*> forme; public: Editor(void){}; void insert(Forma* obj){forme.insert(forme.end(), obj);}; double calculeazaArieTotala(){ Arie arie; for(std::list<Forma *>::iterator it = forme.begin(); it != forme.end(); ++it){ (*it)->accept(&arie); } return arie.getResult(); }; double calculeazaPerimetrulTotal(){ Perimetru perimetru; for(std::list<Forma *>::iterator it = forme.begin(); it != forme.end(); ++it){ (*it)->accept(&perimetru); } return perimetru.getResult(); }; ~Editor(void){ for(auto it = forme.begin(); it != forme.end(); ++it){ delete *it; } }; }; int main(void){ Editor editor; editor.insert(new Cerc()); editor.insert(new Dreptunghi()); std::cout << "Arie totala: " << editor.calculeazaArieTotala() << std::endl; std::cout << "Perimetru total: " << editor.calculeazaPerimetrulTotal() << std::endl; return 0; }
额外优化说明
- 给基类添加了虚析构函数,确保通过基类指针删除子类对象时,子类的析构函数能被正确调用。
- 使用
override关键字显式标记重写的虚函数,提升代码可读性,同时让编译器帮你检查是否正确重写了基类方法。
内容的提问来源于stack exchange,提问作者Sitram
相关产品推荐
相关产品推荐

