【问题标题】:Unidentified identifier c++, but it's identified未识别的标识符 c++,但已识别
【发布时间】:2015-11-05 03:15:56
【问题描述】:

在 Map.h 文件的以下代码中未识别国家或大陆的问题

地图.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

无论是否添加注释掉的包含,我仍然得到错误。

这是另外两个文件

大陆.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

国家.h:

#ifndef COUNTRY_H
#define COUNTRY_H
#include "Continent.h"


using namespace std;

class Country{
    private:
        static int nextCountryNumber;
        int countryNumber;
        Map map;
        string name;
        int x, y;
        Continent continent;
        vector<Country> adjacents;
    public:
        Country(string, int, int, Continent&, Map&);
        string getName();
        int getX();
        int getY();
        Continent getContinent();
        bool isAdjacentTo(Country&);
        bool hasAdjacent();
        void addAdjacent(Country&);
        string toString();
};
#endif

我认为它是某种循环引用,但我找不到这种情况......

【问题讨论】:

  • 为什么要在 Continent.h 中包含 Map.h?
  • @XiaotianPei,因为Map在Country中使用,Country.h包含Continent.h,其中包含Map.h
  • 通常你在标题中使用前向声明而不是include。否则你可能会遇到循环依赖。另外,具体是什么错误?可以贴在这里吗?
  • 不是您的主要问题,但Map(std::ifstream); 可能会给您带来麻烦。在 C++11 之前不允许按值传递流,并且一些主流编译器实现该功能的速度很慢。 Map(std::ifstream &amp;) 会更常见。

标签: c++ undeclared-identifier


【解决方案1】:

通常在 .h 文件中您不想包含其他头文件。它们只是声明,所以它们不需要知道其他类的样子。删除 #include "blah.h" 并在需要的地方编写前向声明 class blah;。这是这种情况除非您将对象blah 直接存储在您的类中,在这种情况下您需要包含blah.h,因为编译器需要知道该对象有多大。如果您改为存储指向对象的指针而不是直接存储对象,则可以避免这些包含(实际上这是一种更常见的做法,因为您不需要在复制构造时复制所有数据)。

编辑:作为旁注,您通常只想包含必要的 #includes(即 iostream),也不要在标题中使用 using namespace std;,因为包含您的标题的另一个文件可能不想使用 std命名空间。相反,在你的实现文件中做这些事情。我在下面做了相应的修改。

编辑 2:另外,请记住 map 是 std 中的一种数据结构类型,因此在命名 map 时要小心(我建议使用不同的名称)。

编辑 3:正如 cmets 标准容器中所指出的,它们需要完整声明它们包含的对象,因此无论何时拥有 vector&lt;blah&gt;,都必须包含 blah.h。还要考虑这一点:在自己的声明中包含一个对象blah 是有问题的。由于对象包含自身的一个实例,这是递归的,那么应该分配多少空间呢?您可以通过在blah 中添加pointer-to-blah 来解决此问题(指针只是一个固定大小的int)。出于类似的原因,Country 类包含vector&lt;Country&gt; 是有问题的。这可以通过将vector&lt;Country&gt; 更改为vector&lt;Country*&gt; 来解决。因此,为了使这些在技术上可以通过 C++ 标准进行编译并清理设计以包含其他头文件,我已经将引用 Country、Continent 和 Map 的成员变量更改为它们各自的指针,一个变化您将需要在实现文件中进行镜像。此外,我已按照另一条评论的建议将 Map(std::ifstream); 修改为 Map(std::ifstream &amp;);,因为我严重怀疑您打算在此处复制 ifstream。

代码:

地图.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

大陆.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

国家.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

【讨论】:

  • 要使这个成为一个好的答案,您需要删除 using namespace std; 语句。这通常是一种糟糕的形式,在头文件中也是极其糟糕的形式。
  • 哈哈,我刚刚做了这些编辑。我们在想同样的事情。
  • 您还需要做三个更改: (1) Continent.h 不需要转发声明Map。 (2) Continent.h 需要转发声明Country。 (3) Country.h 需要到#include Map.h 和 Continent.h 看数据结构。 Continent 类根本不引用 Map,但它确实引用了 Country。 Country 类具有 Map 和 Continent 类型的数据成员,因此前向声明是不够的。
  • 不错的收获。实际上,Country.h 也需要包含Continent :( 我习惯于Objective-C,其中对象是自动指针;你知道在C++ 中通过指针存储是否通常更好?
  • 此建议不正确。 std::vector 不能有不完整的类型作为模板参数。 (尽管有些编译器似乎接受它)。需要组织代码,以便类定义在出现vector&lt;Country&gt; countries; 行的位置可见,依此类推。
【解决方案2】:

我认为它是某种循环引用,但我找不到这种情况......

你有比循环引用更糟糕的问题。你有一个纠结的烂摊子。您可以按照@personjerry 的回答来解决循环引用问题。这无助于解决你纠结的问题。

后一个问题:您的类Country 具有Map 和Continent 类型的数据成员以及相邻Country 对象的向量。你的班级Continent 有一个Country 对象在那个大陆的向量。您的类Map 具有Country 和Continent 对象的向量。 Country 在 Map 对象的 countries 数据成员中名为 Foo 与 Country 在 Continent 对象的 countries 数据成员中名为 Foo 的对象不同。更糟糕的是,您所有的吸气剂都会返回副本。你有一个又一个的副本。这是一个纠结的烂摊子。

解决此问题的 C++11 之前的方法是使用指针并仔细考虑谁拥有什么。在您的情况下,Map 类似乎是主要的,因为这是从输入流构造的类。 Country 类中对Continent 和Map 的引用应该是指针或引用。 Country 和 Continent 类中的 Country 对象的向量应该是指针向量。

更现代的方法是使用智能指针(在您的情况下为共享指针)。人们仍然需要考虑一下谁拥有什么,这里就是这种情况。循环引用在共享指针方面存在一些问题。这种结构的循环引用很有潜力。


旁白:使用using namespace std; 被广泛认为是不好的形式。当该结构位于头文件中时,它几乎被普遍认为是极其糟糕的形式。

【讨论】:

    【解决方案3】:

    对于初学者,您需要在 Map.h 中转发声明类 Country 和 Continent

    class Country;
    class Continent;
    

    在class Map的声明之前。

    【讨论】:

      【解决方案4】:

      您的代码设计存在一些问题。有递归容器定义。

      首先要注意的是std::vector必须有一个完整的类型作为模板参数。这意味着你不能拥有一个包含自身向量的类;所以你将不得不重新考虑设计:

      class Country
      {
          // ...
          vector<Country> adjacents;
      

      类似的 cmets 适用于您的其他向量:前向声明一个类然后声明它的向量是不够的;除非完全定义了 class Country,否则您不能在 class Map 中包含 vector&lt;Country&gt;。不可能,因为class Country 还包含Map!


      但是,看看你的类和向量设计的其余部分,它看起来更像 Java 或 C# 编码器所做的,其中容器包含对象引用而不是对象。

      特别是,包含Map 的Country 没有多大意义。典型的设计只有一张地图,并且地图包含许多国家。但在您的设计中,每个国家都有自己的地图,与任何其他国家拥有的地图完全分开(并且与任何全球地图完全分开)。

      为了在 C++ 中的许多用户之间共享对单个实例的引用,正确的方法是使用 shared_ptr 作为被引用对象的容器。 (这也限制了您分配对象的方式 - 您必须使用 make_shared 而不是直接声明对象来创建它们。

      我猜你可能想对国家和地图做同样的事情。您宁愿为每个国家/地区拥有一个 Country 对象,以及来自其邻国等的对该国家/地区的各种引用。

      对于需要保存国家/地区列表的国家/地区,另一种解决方案是保存您在需要时查找的国家/地区 ID/名称列表;或使用非标准容器,确实允许声明类型不完整。 Boost Container 库有其中的一些。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-26
        • 1970-01-01
        • 1970-01-01
        • 2012-02-28
        • 1970-01-01
        • 2011-10-06
        • 1970-01-01
        • 2012-06-20
        相关资源
        最近更新 更多