3

having this issue with Country or Continent not being identified in the following code for the Map.h file

Map.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

Whether add in the commented out include, i still get the error.

Here are the other two files

Continent.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

Country.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

I think its some sort of circular referencing but I cant find where that is the case...

  • why do you include Map.h in Continent.h ? – Xiaotian Pei Nov 05 '15 at 03:19
  • @XiaotianPei, because `Map` is used in `Country` and `Country.h` includes `Continent.h` which includes `Map.h` – GreatAndPowerfulOz Nov 05 '15 at 03:28
  • Generally you use forward declarations instead of `include` in headers. Otherwise you can run into circular dependencies. Also, what error is it specifically? Could you paste it here? – personjerry Nov 05 '15 at 03:42
  • Not your main problem, but `Map(std::ifstream);` may cause you trouble. Passing streams by value was not allowed before C++11, and some mainstream compilers have been slow to implement that feature. `Map(std::ifstream &)` would be more usual. – M.M Nov 05 '15 at 04:27

4 Answers4

3

Generally in .h files you don't want to include other header files. They're just declarations, so they don't need to know what the other classes look like. Remove the #include "blah.h" and instead write forward declarations class blah; where you need them. This is the case UNLESS you store an object blah directly in your class, in which case you need to include blah.h because the compiler needs to know how big that object is. You can avoid these includes if instead you store pointer-to-object instead of the object directly (and indeed this is a more common practice, as you don't need to copy all the data when you copy construct).

EDIT: As a side note, you generally only want to include the necessary #includes (namely iostream), and also not use using namespace std; in your headers, because another file including your header might not want to use the std namespace. Instead, do these things in your implementation file. I've made the corresponding changes below.

EDIT 2: Also, keep in mind that map is a data structure type in std, so be careful when you name things map (I'd advise using a different name).

EDIT 3: As pointed out in the comments std containers require the full declaration of the objects they contain, so whenever you have vector<blah> you must include blah.h. Also consider this: it's problematic to include an object blah within its own declaration. Since the object contains an instance of itself, that's recursive, so how much space should be allocated? You can sorta fix this by having pointer-to-blah in blah (a pointer is just an int so fixed size). For a similar reason it's problematic for the Country class to contain a vector<Country>. THAT can be solved by changing vector<Country> to vector<Country*>. So to make these technically compilable by the C++ standard and to clean up the design with respect to including other headers, I've changed the member variables referencing Country, Continent, and Map to their respective pointers, a change that you will need to mirror in the implementation files. Also I've modified Map(std::ifstream); to Map(std::ifstream &); as suggested by another comment, as I seriously doubt you intended to copy an ifstream here.

Code:

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
personjerry
  • 1,045
  • 8
  • 28
  • 1
    To make this a good answer you need to remove the `using namespace std;` statement. That is bad form in general, and extremely bad form in a header file. – David Hammen Nov 05 '15 at 03:49
  • Haha, I just made those edits. We were thinking the same thing. – personjerry Nov 05 '15 at 03:52
  • Three more changes you need to make: (1) Continent.h does not need to forward declare `Map`. (2) Continent.h needs to forward declare `Country`. (3) Country.h needs to `#include` Map.h and Continent.h Look at the data structures. Class `Continent` does not reference `Map` at all, but it does reference `Country`. Class `Country` has data members of type `Map` and `Continent`, so a forward declaration is not sufficient. – David Hammen Nov 05 '15 at 03:57
  • Nice catches. Actually, `Country.h` needs to include `Continent` too :( I'm used to Objective-C where objects are automatically pointers; do you know if it's generally better practice in C++ to store via pointer here? – personjerry Nov 05 '15 at 04:00
  • 1
    This suggestion is incorrect. `std::vector` cannot have an incomplete type as the template parameter. (Notwithstanding the fact that some compilers may appear to accept it). The code needs to be organized so that the class definition is visible at the point where the line `vector countries;` appears, and so on. – M.M Nov 05 '15 at 04:24
  • Oh wow, I did not know that. In that case, is `std::vector adjacents;` within `Country` problematic? – personjerry Nov 05 '15 at 04:29
  • @personjerry Yes that is also problematic – M.M Nov 05 '15 at 04:38
  • Okay, I've updated the post with the new info. I've changed them all to pointers, but of course that requires an implementation change (must be done for Country, and definitely beneficial long-run for the others). – personjerry Nov 05 '15 at 05:03
1

I think its some sort of circular referencing but I cant find where that is the case...

You have much worse than a circular referencing problem. You have a tangled mess. You could fix the circular referencing problem by following @personjerry's answer. This will not help solve your tangled mess problem.

The latter problem: Your class Country has data members of type Map and Continent and a vector of adjacent Country objects. Your class Continent has a vector of Country objects in that continent. Your class Map has vectors of Country and Continent objects. The Country named Foo in a Map object's countries data member is not the same object as the Country named Foo in a Continent object's countries data member. Even worse, all of your getters return copies. You have copies upon copies upon copies. This is a tangled mess.

The pre-C++11 way to solve this problem is to use pointers and to think very carefully of who owns what. In your case, the class Map appears to be primary as this is the class that is constructed from an input stream. The references to Continent and Map in class Country should be pointers or references. The vectors of Country objects in classes Country and Continent should be vectors of pointers.

The more modern approach is to use smart pointers (shared pointers in your case). One still needs to think a bit about who owns what, and that's very much the case here. Circular references present a bit of a problem with regard to shared pointers. You have a big potential for circular references with this structure.


Aside: Using using namespace std; is widely considered to be bad form. It is almost universally considered to be extremely bad form when that construct is in a header file.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
0

For starters, in Map.h you need to forward declare classes Country and Continent

class Country;
class Continent;

before the declaration of class Map.

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
GreatAndPowerfulOz
  • 1,767
  • 13
  • 19
0

There's some issues in your code design. There are recursive container definitions.

The first main point to be aware of is that std::vector must have a complete type as the template parameter. This means you cannot have a class that contains a vector of itself; so you will have to rethink the design:

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

Similar comments apply to your other vectors: it's not sufficient to forward-declare a class and then declare a vector of it; you can't have vector<Country> inside class Map unless class Country is fully defined. Which it can't be, because class Country also contains a Map !


However, looking at the rest of your class and vector design, it looks more like what a Java or C# coder would do, where the container contains object references rather than objects.

In particular, Country containing a Map doesn't make much sense. A typical design would only have a single map, and the map contains many countries. But in your design, each country has its own map, completely separate to the map that any other country has (and completely separate to any global map).

In order to share references to a single instance amongst many users in C++, the right way is to use shared_ptr as a container for the object being referenced. (This also restricts how you allocate the objects - you must create them with make_shared rather than with direct declarations of the object).

I'm guessing you probably want to do the same thing with the Countries as well as the Maps. You'd rather have one Country object for each country, and various references to that country from its neighbours etc.

An alternative solution for Country requiring to hold a list of Countries would be to either hold a list of country ID/names that you look up when required; or to use a non-standard container which does permit declaration with incomplete type. The Boost Container library has some of these.

M.M
  • 138,810
  • 21
  • 208
  • 365