C++前向声明避头文件递归却触发不完全类型使用错误排查
问题描述
我定义了Fahrzeug(车辆)和Weg(道路)两个类,Weg类通过列表存储行驶在其上的Fahrzeug对象。由于两类互相依赖,我采用在头文件中前向声明对方类、仅在.cpp文件中包含头文件的方式避免头文件递归,但创建Weg实例时触发错误:
\main.cpp:253:15: error: invalid use of incomplete type 'class Weg'
排查构造函数初始化列表未发现问题,相关代码如下:
相关代码
Simulationsobjekt.h
/* * Simulationsobjekt.h * * Created on: 11.10.2022 * Author: nikid */ #ifndef SIMULATIONSOBJEKT_H_ #define SIMULATIONSOBJEKT_H_ #include <iostream> class Simulationsobjekt { public: Simulationsobjekt(const std::string p_sName); virtual ~Simulationsobjekt(); Simulationsobjekt(const Simulationsobjekt&) = delete; virtual void vSimulieren() = 0; virtual const void vAusgeben(std::ostream& ostream); virtual void operator=(const Simulationsobjekt& simulationsobjekt); virtual bool operator==(const Simulationsobjekt& imulationsobjekt); protected: std::string p_sName = ""; int p_iID = 0; static int p_iMaxID; //noetig? double p_dGesamtZeit = 0.0; }; //Operator muss global geschrieben werden, da er zu ostream und nicht Fahrzeug gehoert //Es reicht aus es einmal fuer die Parent Klasse zu schreiben std::ostream& operator<<(std::ostream& ostream, Simulationsobjekt& simulationsobjekt); #endif /* SIMULATIONSOBJEKT_H_ */
Simulationsobjekt.cpp
/* * Simulationsobjekt.cpp * * Created on: 11.10.2022 * Author: nikid */ #include "Simulationsobjekt.h" #include <iomanip> #define IDWIDTH 7 #define NAMEWIDTH 15 int Simulationsobjekt::p_iMaxID = 0; Simulationsobjekt::Simulationsobjekt(const std::string p_sName) : p_iID(p_iMaxID) { this->p_sName = p_sName; p_iMaxID++; //Ausgabe std::cout << "Objekt (Name: " << p_sName << ", ID: " << p_iID << ") erzeugt" << std::endl; } Simulationsobjekt::~Simulationsobjekt() { //Ausgabe std::cout << "Objekt (Name: " << p_sName << ", ID: " << p_iID << ") geloescht" << std::endl; } const void Simulationsobjekt::vAusgeben(std::ostream& ostream) { ostream << std::setw(IDWIDTH) << p_iID << std::setw(NAMEWIDTH) << p_sName; } void Simulationsobjekt::operator=(const Simulationsobjekt& simulationsobjekt) { p_sName = simulationsobjekt.p_sName; p_iID = p_iMaxID++; } bool Simulationsobjekt::operator==(const Simulationsobjekt& simulationsobjekt) { return p_iID == simulationsobjekt.p_iID; } std::ostream& operator<<(std::ostream& ostream, Simulationsobjekt& simulationsobjekt) { simulationsobjekt.vAusgeben(ostream); return ostream; }
Weg.h
/* * Weg.h * * Created on: 11.10.2022 * Author: nikid */ #ifndef WEG_H_ #define WEG_H_ #include "Simulationsobjekt.h" #include "Tempolimit.h" #include <list> #include <memory> //noetig fuer unique pointer etc. class Fahrzeug; //statt Header einbinden um circular dependecies zu vermeiden class Weg : Simulationsobjekt { public: Weg(); Weg(std::string p_sName, double p_dLaenge); Weg(std::string p_sName, double p_dLaenge, Tempolimit tempolimit); //wie mache ich tempolimit optional??? virtual ~Weg(); double getTempolimit(Tempolimit p_eTempolimit); void vSimulieren() override; static const void vKopf(); const void vAusgeben(std::ostream& ostream) override; //int getTempolimit(Tempolimit p_eTempolimit); protected: const double p_dLaenge = 0.0; std::list<std::unique_ptr<Fahrzeug>> p_pFahrzeuge; //Welche Art Liste? Tempolimit p_eTempolimit; }; #endif /* WEG_H_ */
Weg.cpp
/* * Weg.cpp * * Created on: 11.10.2022 * Author: nikid */ #include "Weg.h" #include "Fahrzeug.h" #include <iomanip> #define LAENGEWIDTH 10 #define IDWIDTH 7 #define NAMEWIDTH 15 #define FAHRZEUGEWIDTH 20 Weg::Weg() : Simulationsobjekt(""), p_eTempolimit(Tempolimit::Autobahn) {} Weg::Weg(std::string p_sName, double p_dLaenge) : Simulationsobjekt(p_sName), p_eTempolimit(Tempolimit::Autobahn), p_dLaenge(p_dLaenge) { } Weg::Weg(std::string p_sName, double p_dLaenge, Tempolimit tempolimit) : Simulationsobjekt(p_sName), p_eTempolimit(tempolimit), p_dLaenge(p_dLaenge) { } Weg::~Weg() { } double Weg::getTempolimit(Tempolimit p_eTempolimit) { return (double)p_eTempolimit; } void Weg::vSimulieren() { /* for (auto& f : p_pFahrzeuge) { f->vSimulieren(); //simulieren aller Fahrzeuge. } */ } const void Weg::vKopf() { std::string separator = "----------------------------------------------------------------------------------------------------- "; std::cout << std::left << std::setw(IDWIDTH) << "ID" << std::setw(NAMEWIDTH) << "Name" << std::setw(LAENGEWIDTH) << "Laenge" << std::setw(FAHRZEUGEWIDTH) << "Fahrzeuge" << std::endl; } const void Weg::vAusgeben(std::ostream& ostream) { Simulationsobjekt::vAusgeben(ostream); ostream << std::setw(LAENGEWIDTH) << p_dLaenge; ostream << "("; for (auto& f : p_pFahrzeuge) { ostream << f->getName(); } ostream << ")"; }
Fahrzeug.h
/* * Fahrzeug.h * * Created on: 01.10.2022 * Author: nikid */ #ifndef FAHRZEUG_H_ #define FAHRZEUG_H_ #include <iostream> #include <limits> #include "Simulationsobjekt.h" class Weg; class Fahrzeug : public Simulationsobjekt { public: Fahrzeug(); Fahrzeug(const std::string p_sName); Fahrzeug(const std::string p_sName, const double p_dMaxGeschwindingkeit); Fahrzeug(const Fahrzeug&) = delete; //sinnvoll, da sonst eine Kopie (Duplikat) vorliegt, welche die selbe ID besitzt virtual ~Fahrzeug(); virtual const void vAusgeben(std::ostream& ostream); static const void vKopf(); virtual void vSimulieren(); virtual double dTanken(const double dMenge = std::numeric_limits<double>::infinity()); virtual double dGeschwindigkeit(); bool operator<(const Fahrzeug& fahrzeug); void operator=(const Fahrzeug& fahrzeug); //GETTER & SETTER const std::string getName(); protected: double p_dMaxGeschwindigkeit = 0.0; //const weg? double p_dGesamtStrecke = 0.0; double p_dZeit = 0.0; }; #endif /* FAHRZEUG_H_ */
Fahrzeug.cpp
/* * Fahrzeug.cpp * * Created on: 01.10.2022 * Author: nikid */ #include "Fahrzeug.h" #include "Header.h" #include "Weg.h" #include <iomanip> // std::setiosflags, std::resetiosflags #define IDWIDTH 7 #define NAMEWIDTH 15 #define MAXVWIDTH 20 #define GESSTRECKEWIDTH 15 #define GESVERBRAUCHWIDTH 17 #define TANKINHALTWIDTH 12 #define VWIDTH 17 Fahrzeug::Fahrzeug() : Simulationsobjekt("") { //p_iMaxID++; kein Counter, sondern bloss fuer ID-Vergabe gedacht } Fahrzeug::Fahrzeug(const std::string sName) : Simulationsobjekt(sName) { //p_iMaxID++; } Fahrzeug::Fahrzeug(const std::string sName, const double dMaxGeschwindigkeit) : Simulationsobjekt(sName), p_dMaxGeschwindigkeit((dMaxGeschwindigkeit > 0) ? dMaxGeschwindigkeit : -1 * dMaxGeschwindigkeit) { //p_iMaxID++; } Fahrzeug::~Fahrzeug() { } const void Fahrzeug::vAusgeben(std::ostream& ostream) { Simulationsobjekt::vAusgeben(ostream); ostream << std::setw(MAXVWIDTH) << p_dMaxGeschwindigkeit << std::setw(GESSTRECKEWIDTH) << p_dGesamtStrecke << std::setw(VWIDTH) << dGeschwindigkeit(); } const void Fahrzeug::vKopf() { std::string separator = "----------------------------------------------------------------------------------------------------- "; std::cout << std::left << std::setw(IDWIDTH) << "ID" << std::setw(NAMEWIDTH) << "Name" << std::setw(MAXVWIDTH) << "MaxGeschwindigkeit" << std::setw(GESSTRECKEWIDTH) << "Gesamtstrecke" << std::setw(VWIDTH) << "Geschwindigkeit" << std::setw(TANKINHALTWIDTH) << "Tankinhalt" << std::setw(GESVERBRAUCHWIDTH) << "Gesamtverbrauch" << std::endl; std::cout << separator; } void Fahrzeug::vSimulieren() { double dZeitDifferenz = dGlobaleZeit - p_dZeit; //Gesamtzeit um vergangene zeit erhoehen if(dZeitDifferenz == 0) return; if(dGeschwindigkeit() > 0) { p_dGesamtZeit += dZeitDifferenz; } p_dZeit = dGlobaleZeit; p_dGesamtStrecke += dGeschwindigkeit() * dZeitDifferenz; //Strecke = v * dt } double Fahrzeug::dTanken(double dMenge) { return 0.0; } double Fahrzeug::dGeschwindigkeit() { return p_dMaxGeschwindigkeit; } bool Fahrzeug::operator<(const Fahrzeug& fahrzeug) { return p_dGesamtStrecke < fahrzeug.p_dGesamtStrecke; } void Fahrzeug::operator=(const Fahrzeug& fahrzeug) { p_sName = fahrzeug.p_sName; p_dMaxGeschwindigkeit = fahrzeug.p_dMaxGeschwindigkeit; p_iID = p_iMaxID++; } const std::string Fahrzeug::getName() { return p_sName; }
Tempolimit.h
/* * Tempolimit.h * * Created on: 12.10.2022 * Author: nikid */ #ifndef TEMPOLIMIT_H_ #define TEMPOLIMIT_H_ #include <limits> enum class Tempolimit { Innerorts = 50, Landstrasse = 100, Autobahn = std::numeric_limits<int>::max() // unbegrenzt/unendlich }; #endif /* TEMPOLIMIT_H_ */
错误原因及解决方案
核心错误原因
invalid use of incomplete type 'class Weg'错误是因为编译main.cpp时,编译器只看到Weg的前向声明,没有获取到类的完整定义。前向声明只能告诉编译器“这个类存在”,但无法提供类的大小、成员函数实现等信息,因此无法实例化对象。
具体修复步骤
在
main.cpp中包含Weg.h头文件
在main.cpp的开头添加:#include "Weg.h"只有包含头文件,编译器才能获取
Weg类的完整定义,从而正确创建实例。修正
Weg类的继承方式
在Weg.h中,将默认的私有继承改为公有继承:class Weg : public Simulationsobjekt { // 原有代码保持不变 };C++中类默认继承权限是私有,而
Simulationsobjekt是抽象基类,私有继承会导致Weg无法被当作Simulationsobjekt使用,也无法正确实现多态,公有继承才是符合预期的选择。修复其他潜在问题
- 简化
Weg::getTempolimit函数:当前函数接收冗余参数,应改为返回成员变量p_eTempolimit的值:// Weg.h中修改声明 double getTempolimit() const; // Weg.cpp中修改实现 double Weg::getTempolimit() const { return static_cast<double>(p_eTempolimit); } - 修正
Fahrzeug构造函数的拼写错误:将p_dMaxGeschwindingkeit改为p_dMaxGeschwindigkeit,避免初始化错误。
- 简化
内容的提问来源于stack exchange,提问作者insertRandomName
相关产品推荐
相关产品推荐

