让我们说,我有一个标有模板(通用)类型数据的个人世界。我有一个特定的个人,称为国王。所有个人都应该能够在国王面前跪下。一般来说,个人可以被标记为任何东西。国王用数字标记(第1,第2国王)。
错误
g++ -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H -c -o Individual.o Individual.cpp
g++ -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H -c -o King.o King.cpp
In file included from King.h:3,
from King.cpp:2:
Individual.h: In member function ‘void Individual<Data>::KneelBeforeTheKing(King*)’:
Individual.h:21: error: invalid use of incomplete type ‘struct King’
Individual.h:2: error: forward declaration of ‘struct King’
make: *** [King.o] Error 1
Individual.h(Individual.cpp为空)
//Individual.h
#pragma once
class King;
#include "King.h"
#include <cstdlib>
#include <cstdio>
template <typename Data> class Individual
{
protected:
Data d;
public:
void Breathe()
{
printf("Breathing...\n");
};
void KneelBeforeTheKing(King* king)
{
king->CommandToKneel();
printf("Kneeling...\n");
};
Individual(Data a_d):d(a_d){};
};
King.h
//King.h
#pragma once
#include "Individual.h"
#include <cstdlib>
#include <cstdio>
class King : public Individual<int>
{
protected:
void CommandToKneel();
public:
King(int a_d):
Individual<int>(a_d)
{
printf("I am the No. %d King\n", d);
};
};
King.cpp
//King.cpp
#include "King.h"
#include <string>
int main(int argc, char** argv)
{
Individual<std::string> person("Townsperson");
King* king = new King(1);
king->Breathe();
person.Breathe();
person.KneelBeforeTheKing(king);
}
void King::CommandToKneel()
{
printf("Kneel before me!\n");
}
生成文件
CXX = g++
CXXFLAGS = -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H
OBJS = Individual.o King.o
test: $(OBJS)
$(CXX) -o $@ $^
clean:
rm -rf $(OBJS) test
all: test
答案 0 :(得分:3)
error: invalid use of incomplete type ‘struct King’
这意味着您只声明King
而未定义它。您有循环包含问题(Individual.h
和King.h
互相包含)。 This article应该对这个问题有所了解。
答案 1 :(得分:3)
你的两个班级King和Individual紧密相连。
将它放在两个标题中是不行的,因为两者都需要彼此。
如果您的课程必须以这种方式设计,那么:
首先定义类Individual但不实现KneelBeforeTheKing,只需声明该函数。
然后定义King
然后实施上述方法。
但是你的设计可能都错了。例如,您的模板类有许多不依赖于篡改类型的方法,包括KneelBeforeTheKing,并且应该从模板中重构。
答案 2 :(得分:2)
嗯,这里的问题与模板没有关系。
您正在基类中调用后代的专用功能。这几乎总是一个糟糕的设计标志。
反转课程怎么样?这样的事情(可能包含错误):
template < typename PersonType >
class Person : PersonType
{
public:
void command_to_kneel() { PersonType::command_to_kneel(); }
void kneel_before_king(Person<King>* king) { king->command_to_kneel(); }
void breathe() { }
};
int main()
{
Person<King> king;
Person<Knight> knight;
knight.kneel_before_king(&king);
}
答案 3 :(得分:1)
您可以通过向类King添加另一个基类(例如NobleBase)来打破依赖关系:
struct NobleBase
{
virtual void KneelBefore() = 0;
};
class King : public Individual<int>,
public NobleBase
并改变方法
void KneelBeforeTheKing(King* king)
到这个
void KneelBeforeTheKing(NobleBase* king)
然后你需要包含NobleBase类的标题。
答案 4 :(得分:0)
除了语法和编译器错误之外,我认为最大的问题是你的个人告诉国王告诉自己跪下。
void KneelBeforeTheKing(King* king)
{
king->CommandToKneel();
printf("Kneeling...\n");
};
正如Let_Me_Be所指出的,这是设计而不是语法的问题。实际上,国王应该决定个人何时下跪,所以任何对CommandToKneel()的调用都来自King本人,而个人的集合作为一个参数来指定谁被命令是有道理的。
语法问题是您尝试使用尚未定义的功能。 King的所有前向声明都是告诉个人的头文件,有一个名为King的类型。此时编译器对King的成员一无所知,因为你没有包含声明这些成员的头文件,也没有在使用之前将King定义在任何地方。
然而,正如已经指出的那样,在其基本类型(即个人)的声明中对派生类型(即King)进行任何类型的引用都是错误的设计。基类应该不知道它的派生类型。