在Map.h文件的以下代码中没有标识Country或Continent的此问题
Map.h:
myTextBlock.SetValue(Grid.ColumnProperty, "foo");
是否添加注释掉包含,我仍然会收到错误。
以下是其他两个文件
Continent.h:
#ifndef MAP_H
#define MAP_H
#include<string>
#include<vector>
#include<iostream>
#include<fstream>
//#include "Country.h"
using namespace std;
class Map{
private:
vector<Country> countries;
vector<Continent> continents;
vector<vector<int> > adjacents;
string author;
string image;
string wrap;
string scroll;
string warn;
public:
Map(ifstream);
Map();
void save();
void setAdjacent(Country&, Country&);
void placeWithin(Continent&, Country&);
int numOfCountries();
int numOfContinents();
bool verify();
bool isAdjacent(Country*, Country&);
bool hasAdjacent(Country*);
bool hasCountry(Continent&);
};
#endif
Country.h:
#ifndef CONTINENT_H
#define CONTINENT_H
#include "Map.h"
using namespace std;
class Continent
{
private:
string name;
int bonus;
vector<Country> countries;
public:
void addCountry(Country&);
int getOwner();
int getBonus();
int getSize();
string getName();
bool hasCountry(Country&);
};
#endif
我认为它是某种循环引用,但我无法找到那种情况......
答案 0 :(得分:3)
通常在.h文件中,您不希望包含其他头文件。它们只是声明,所以他们不需要知道其他类的外观。删除#include "blah.h"
,然后在您需要的地方写前向声明class blah;
。这是 UNLESS 直接在您的类中存储对象blah
的情况,在这种情况下,您需要包含blah.h
,因为编译器需要知道该对象有多大。你可以避免使用这些包含而不是直接存储指向对象而不是对象(实际上这是一种更常见的做法,因为在复制构造时不需要复制所有数据)。
编辑:作为旁注,您通常只想包含必要的#includes
(即iostream),也不要在标题中使用using namespace std;
,因为包含您标题的其他文件可能不需要使用std命名空间。相反,在您的实现文件中执行这些操作。我在下面做了相应的修改。
编辑2:另外,请记住map
是std中的数据结构类型,因此在命名map
时要小心(我建议使用其他名称)。
编辑3:正如评论中所指出的那样,std容器需要完整声明它们包含的对象,所以只要你有vector<blah>
,就必须包含blah.h
。还要考虑这一点:在自己的声明中包含一个对象blah
是有问题的。由于对象包含自身的实例,这是递归的,所以应该分配多少空间?您可以通过在pointer-to-blah
中使用blah
来对此进行排序(指针只是一个固定大小的int)。出于类似的原因,Country
类包含vector<Country>
会有问题。可以通过将vector<Country>
更改为vector<Country*>
来解决这个问题。因此,为了使这些技术可以通过C ++标准进行编译并清理包含其他标题的设计,我已将引用Country
,Continent
和Map
的成员变量更改为他们各自的指针,您需要在实现文件中镜像的更改。另外,我已根据其他评论的建议将Map(std::ifstream);
修改为Map(std::ifstream &);
,因为我严重怀疑您打算在此处复制ifstream。
代码:
Map.h:
#ifndef MAP_H
#define MAP_H
#include<string>
#include<vector>
#include<fstream>
class Country;
class Continent;
class Map{
private:
std::vector<Country*> countries; // consider pointers instead
std::vector<Continent*> continents; // consider pointers instead
std::vector<std::vector<int> > adjacents; // (just ints, so no pointers needed)
std::string author;
std::string image;
std::string wrap;
std::string scroll;
std::string warn;
public:
Map(std::ifstream &);
Map();
void save();
void setAdjacent(Country&, Country&);
void placeWithin(Continent&, Country&);
int numOfCountries();
int numOfContinents();
bool verify();
bool isAdjacent(Country*, Country&);
bool hasAdjacent(Country*);
bool hasCountry(Continent&);
};
#endif
Continent.h:
#ifndef CONTINENT_H
#define CONTINENT_H
#include<string>
#include<vector>
class Country;
class Continent
{
private:
std::string name;
int bonus;
std::vector<Country*> countries;
public:
void addCountry(Country&);
int getOwner();
int getBonus();
int getSize();
std::string getName();
bool hasCountry(Country&);
};
#endif
Country.h:
#ifndef COUNTRY_H
#define COUNTRY_H
#include<string>
#include<vector>
class Map;
class Continent;
class Country{
private:
static int nextCountryNumber;
int countryNumber;
Map *map;
std::string name;
int x, y;
Continent *continent;
std::vector<Country*> adjacents;
public:
Country(std::string, int, int, Continent&, Map&);
std::string getName();
int getX();
int getY();
Continent getContinent();
bool isAdjacentTo(Country&);
bool hasAdjacent();
void addAdjacent(Country&);
std::string toString();
};
#endif
答案 1 :(得分:1)
我认为它是某种循环引用,但我无法找到那种情况......
你比循环引用问题更糟糕。你有一个纠结的混乱。您可以通过关注@ personjerry的答案来解决循环引用问题。这无助于解决纠结的混乱问题。
后一个问题:您的班级Country
包含Map
和Continent
类型的数据成员以及相邻Country
个对象的向量。您的班级Continent
在该大陆中有Country
个对象的向量。您的班级Map
包含Country
和Continent
个对象的向量。 Country
对象的Foo
数据成员中名为Map
的{{1}}与名为countries
的{{1}}的对象不同Country
对象的Foo
数据成员。更糟糕的是,所有的getter都会返回副本。副本上有副本。这是一个混乱的混乱。
解决这个问题的前C ++ 11方法是使用指针并仔细考虑谁拥有什么。在您的情况下,类Continent
似乎是主要的,因为这是从输入流构造的类。类countries
中对Map
和Continent
的引用应该是指针或引用。类Map
和Country
中的Country
个对象的向量应该是指针的向量。
更现代的方法是使用智能指针(在您的情况下为共享指针)。人们仍然需要考虑一下谁拥有什么,这就是这里的情况。循环引用对共享指针提出了一些问题。具有这种结构的循环引用具有很大的潜力。
除此之外:使用Country
被广泛认为是不好的形式。当该构造位于头文件中时,几乎普遍认为它是非常糟糕的形式。
答案 2 :(得分:0)
对于初学者,在Map.h
中,您需要转发声明类Country
和Continent
class Country;
class Continent;
在宣布class Map
之前。
答案 3 :(得分:0)
您的代码设计存在一些问题。有递归容器定义。
要注意的第一个要点是std::vector
必须具有完整类型作为模板参数。这意味着你不能拥有一个包含自身向量的类;所以你必须重新考虑设计:
class Country
{
// ...
vector<Country> adjacents;
类似的评论适用于你的其他载体:前向声明一个类然后声明它的向量是不够的;除非vector<Country>
已完全定义,否则class Map
内class Country
不能class Country
。它不可以,因为Map
还包含Country
!
然而,看看你的其他类和矢量设计,它看起来更像是Java或C#编码器会做什么,其中容器包含对象引用而不是对象。
特别是,包含Map
的{{1}}没有多大意义。典型设计只有一个地图,地图包含许多国家/地区。但在您的设计中,每个国家/地区都有自己的地图,与任何其他国家/地区的地图完全分开(并且与任何全球地图完全分开)。
为了在C ++中的许多用户之间共享对单个实例的引用,正确的方法是使用shared_ptr
作为被引用对象的容器。 (这也限制了您分配对象的方式 - 您必须使用make_shared
而不是直接声明对象来创建它们。
我猜你可能想和国家和地图做同样的事情。您宁愿为每个国家/地区拥有一个国家/地区对象,以及来自其邻居等的对该国家/地区的各种引用。
要求持有国家名单的国家的替代解决方案是要么保留您在需要时查找的国家/地区ID /名称列表;或者使用 允许声明为不完整类型的非标准容器。 Boost容器库中有一些。