[英]Why am I getting base class undefined?
你好 StackOverflow!
我正在嘗試制作某種代數求解代碼。 我已經列出了一些基本結構。 但是我偶然發現子類發現基類未定義的錯誤。 我試過刪除#pragma once
,並在這里和那里刪除和更改包含語句。 但我無法弄清楚。
我認為這與包括彼此在內的文件有關,但從我認為我以前知道的情況來看。 多虧了#pragma once
和#ifndef
,這應該是可能的。 請告訴我我在這里做錯了什么!
表達式.h:
#pragma once
#ifndef MATH_EXPRESSION_H
#define MATH_EXPRESSION_H
#include <string>
#include "Variable.h"
#include "Value.h"
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0;
virtual operator std::string() const = 0;
template<class T>
bool isType() {
return dynamic_cast<T*>(this);
}
};
#endif
變量.h:
#pragma once
#ifndef MATH_VARIABLE_H
#define MATH_VARIABLE_H
#include "Expression.h"
class Variable : public Expression {
};
#endif
值.h:
#pragma once
#ifndef MATH_VALUE_H
#define MATH_VALUE_H
#include "Expression.h"
class Value : public Expression {
public:
float value;
Value(float value) : value(value) {}
public:
Expression* eval() { return this; }
Expression* eval(const Variable* v, Value* val) {
return this->eval();
}
operator std::string() const {
return std::to_string(value);
}
};
#endif
這是我要解決的構建錯誤:
Variable.h(7,36): error C2504: 'Expression': base class undefined
您的 header 文件之間有循環引用。
如果Expression.h
在包含Variable.h
和Value.h
之前包含在翻譯單元中,則Expression.h
將定義MATH_EXPRESSION_H
保護,然后在聲明Expression
class 之前包含Variable.h
。 Variable.h
將再次包含Expression.h
,由於MATH_EXPRESSION_H
保護,這實際上是一個空操作。 因此, Variable
class 將引用尚未聲明的Expression
類型,因此會出現編譯器錯誤。
如果在包含Expression.h
之前將Variable.h
包含在翻譯單元中,則Variable.h
將定義MATH_VARIABLE_H
保護,然后在聲明Variable
class 之前包含Expression.h
。 Expression.h
將再次包含Variable.h
,由於MATH_VARIABLE_H
保護,這實際上是一個空操作。 Expression.h
然后將包含Value.h
,它將再次包含Expression.h
,這是一個無操作(見上文)。 因此, Value
class 將引用尚未聲明的Expression
類型,因此會出現編譯器錯誤。
如果Value.h
在包含Expression.h
之前包含在翻譯單元中,則Value.h
將定義MATH_VALUE_H
保護,然后在聲明Value
class 之前包含Expression.h
。 Expression.h
將包括Variable.h
,這將再次包括Expression.h
,這是一個空操作(見上文)。 然后Expression.h
將包含Value.h
,由於MATH_VALUE_H
保護,這是一個無操作。 因此, Variable
class 將引用尚未聲明的Expression
類型,因此會出現編譯器錯誤。
讓我們用實際的代碼來看看這個。
如果首先包含Expression.h
,編譯器將在預處理器指令被處理后看到:
// content from <string> ...
class Variable : public Expression { // <-- error, Expression not defined!
};
class Value : public Expression { // <-- error, Expression not defined!
...
public:
Expression* eval() { ... } // <-- error, Expression not defined!
Expression* eval(const Variable* v, Value* val) { ... } // <-- error, Expression not defined!
...
};
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- OK!
};
如果首先包含Variable.h
:
// content from <string> ...
class Value : public Expression { // <-- error, Expression not defined!
...
public:
Expression* eval() { ... } // <-- error, Expression not defined!
Expression* eval(const Variable* v, Value* val) { ... } // <-- error, Expression and Variable not defined!
...
};
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- error, Variable not defined!
...
};
class Variable : public Expression { // <-- OK!
};
如果首先包含Value.h
:
// content from <string> ...
class Variable : public Expression { // <-- error, Expression not defined!
};
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- error, Value not defined!
...
};
class Value : public Expression { // <-- OK!
public:
Expression* eval() { ... } // <-- OK!
Expression* eval(const Variable* v, Value* val) { ... } // <-- OK!
...
};
因此,無論首先包含哪個 header,這都是一個不成功的局面。
由於您僅使用指向 function 參數類型和返回類型中的各種類的指針,並且不訪問類的任何成員,因此您可以通過將Expression.h
中的#include
語句替換為前向聲明來解決此循環問題,例如:
#pragma once
#ifndef MATH_EXPRESSION_H
#define MATH_EXPRESSION_H
#include <string>
// forward declarations
class Variable;
class Value;
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0;
virtual operator std::string() const = 0;
template<class T>
bool isType() {
return dynamic_cast<T*>(this);
}
};
#endif
Expression
的聲明只需要知道Variable
和Value
是存在的,而不是它們里面的樣子。 與Value
的聲明相同,只需要知道Variable
存在,而不是它內部的樣子。
那么,讓我們看看這種變化對編譯器在預處理器指令處理后看到的代碼有什么影響。
如果首先包含Expression.h
:
// content from <string> ...
// forward declarations
class Variable;
class Value;
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- OK!
...
};
如果首先包含Variable.h
:
// content from <string> ...
// forward declarations
class Variable;
class Value;
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- OK!
};
class Variable : public Expression { // OK!
};
如果首先包含Value.h
:
// content from <string> ...
// forward declarations
class Variable;
class Value;
class Expression {
public:
virtual Expression* eval(const Variable* var, Value* val) = 0; // <-- OK!
...
};
class Value : public Expression { // <-- OK!
...
public:
Expression* eval() { ... } // <-- OK!
Expression* eval(const Variable* v, Value* val) { ... } // <-- OK!
...
};
這確實意味着您必須在需要實際訪問Variable
和Value
成員的任何其他源/頭文件中#include
Variable.h
和Value.h
頭文件。
每當您處理僅使用指向類型的指針/引用而不使用該類型的任何成員的聲明時,您應該更喜歡該類型的前向聲明,而不是 header 文件包含。 這將減輕編譯器的負擔,並避免任何潛在的循環問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.