Reputation: 4006
I'm pretty new to C++ and am having trouble making a pointer point from one class to another. This is what I have, it compiles without error, but doesn't work the way I want it to.
JungleMap *Map;
class JungleMap
{
public:
void goNorth()
{
cout << "You are going north towards the river.\n";
delete[] Map;
RiverMap *Map;
}
}
class RiverMap
{
public:
void goNorth()
{
cout << "You are going north away from the river.\n";
delete[] Map;
JungleMap *Map;
}
}
int main()
{
Map->goNorth();
Map->goNorth();
}
This is what the output is:
You are going north towards the river.
You are going north towards the river.
And this is what I would like the output to be:
You are going north towards the river.
You are going north away from the river.
How do I achieve this? It's really bugging me, especially since it compiles without problems.
Upvotes: 0
Views: 626
Reputation: 24249
You appear to be confusing declaration with assignment.
The following line of code is called a declaration, it tells the compiler the properties and attributes of a thing.
JungleMap *Map;
After this line of code, the compiler knows that "Map" is a symbol (a name) referring to a pointer to a JungleMap.
The compiler doesn't have to do anything with a declaration, unless it would have a side effect, at which point it becomes a definition, which means that the declaration invokes a non-trivial constructor or provides an assignment:
struct Foo {};
struct Baz { Baz() { std::cout << "Baz is here\n"; } };
These are declarations - they don't create instances of objects, they describe the layout and functions for instances. At some point you have to create a concrete instance of them with a definition or a call to new
.
struct Foo {};
struct Bar { Bar() { std::cout << "Bar is here\n"; } };
struct Baz {};
int main() {
int i; // no side effects, i is trivial.
char* p; // no side effects, p is a pointer (trivial) type
std::string* sp; // trivial, pointer
Foo f; // trivial
Bar b; // non-trivial, baz has a user-defined ctor that has side-effects.
Bar* bar; // trivial, unassigned pointer type.
Bar* bar2 = new Bar(); // side effects.
Bar bar(); // syntax error, "the most vexing parse"
}
In the above code, we never use "Baz" and we never declare an object of type Baz so the compiler essentially throws it away. Because so many of the variables are trivial and have no side effect, the result of compiling the above will be functionally equivalent to if we had written:
struct Foo {};
struct Bar { Bar() { std::cout << "Bar is here\n"; } };
int main() {
Bar* bar2 = new Bar(); // side effects.
Bar bar(); // syntax error, "the most vexing parse"
}
All of the rest does nothing.
C++ also allows you to re-use names as long as they are in different scopes, but this creates a new, hidden ("shadow") thing:
#include <iostream>
int main() {
int i = 1;
if (i == 1) {
float i = 3.141;
std::cout << "inner i = " << i << '\n';
}
std::cout << "outer i = " << i << '\n';
return 0;
}
The code you wrote will therefore compile, because it is declaring a new and private "Map" inside each of the go functions and then simply never using them.
Note that above I was able to declare i
differently inside the inner scope than the outer.
C++ does not allow you to change the type of a variable - in the above code there are two variables called i. When we created the second i, it is a second variable called i the original variable didn't change.
In order to do what you are trying to do, you're going to need to learn about "polymorphism" and "inheritance", C++ concepts that will allow you to describe a "Room" or "Location" and then base JungleMap and RiverMap on that base definition such that you can take a pointer to the core concept, the Room, and write generic code that deals with rooms while moving the specifics of Jungle, River or BridgeMap into specialized functions. But I think that's beyond the scope of a reply here.
Upvotes: 0
Reputation: 409166
What you should do is to sit down and think about the problem you are trying to solve, and make a proper design. In your case you have two "locations", and the "player" should be able to move between these locations. Starting from that we have identified two possible classes (Location
and Player
) and one behavior (the player can move from location to location).
With the above information, you could do something like this:
class Location
{
public:
void setNorth(Location* loc)
{
north_ = loc;
}
Location* getNorth() const
{
return north_;
}
void setSouth(Location* loc)
{
south_ = loc;
}
Location* getSouth() const
{
return south_;
}
void setDescription(const std::string& descr)
{
description_ = descr;
}
const std::string& getDescription() const
{
return description_;
}
protected:
Location() {} // Made protected to prevent direct creation of Location instances
private:
Location* north_;
Location* south_;
std::string description_;
};
class Jungle : public Location
{
public:
Jungle() : Location()
{
setDescription("You are in a jungle.");
}
};
class River : public Location
{
public:
River() : Location()
{
setDescription("You are close to a river.");
}
};
// The actual "map"
std::vector<Location*> map
void createMap()
{
map.push_back(new Jungle);
map.push_back(new River);
map[0]->setNorth(map[1]);
map[1]->setSouth(map[0]);
}
class Player
{
public:
Player(Location* initialLocation)
: currentLocation_(initialLocation)
{
std::cout << currentLocation_->getDescription() << '\n';
}
...
// Other methods and members needed for a "player"
void goNorth()
{
if (currentLocation_ && currentLocation_->getNorth())
{
currentLocation_ = currentLocation_->getNorth();
std::cout << currentLocation_->getDescription() << '\n';
}
}
void goSouth()
{
if (currentLocation_ && currentLocation_->getSouth())
{
currentLocation_ = currentLocation_->getSouth();
std::cout << currentLocation_->getDescription() << '\n';
}
}
private:
Location* currentLocation_; // The players current location
};
int main()
{
createMap(); // Create the "map"
Player player(map[0]); // Create a player and place "him" in the jungle
// Move the player around a little
player.goNorth();
player.goSouth();
}
In the code above, you have a single player object, which have a "current location". When you move the player around, you simply change the current location for that player. The current location of the player acts as the global Map
variable you have.
Note: I'm not saying that this is a good design or code, just that it's simple.
However, if you're truly new to C++, you should probably start with some simpler problems, including tutorials on pointers and inheritance.
Upvotes: 0
Reputation: 490108
You could make this work (to at least a minimal degree) by creating a base class from which both JungleMap and RiverMap derive. You'd then have a pointer to the base class, which you'd point at an instance of one of the derived classes. You'll also need to rearrange the code somewhat to get it to compile.
class Map {
public:
virtual void goNorth() { cout<<"Sorry, you can't go that way"; }
virtual void goSouth() { cout<<"Sorry, you can't go that way"; }
};
Map *map;
class RiverMap;
class JungleMap : public Map {
public:
void goNorth();
};
class RiverMap : public Map {
public:
void goSouth();
};
void JungleMap::goNorth() {
cout<<"You are going north towards the river.\n";
delete map;
map=new RiverMap;
}
void RiverMap::goSouth() {
cout<<"You are going south towards the jungle.\n";
delete map;
map=new JungleMap;
}
Note: here I'm just trying to say as close to your original design as possible and still have some code that might at least sort of work. I'm certainly not holding it up as an exemplary design, or even close to it (because, frankly, it's not).
Upvotes: 1
Reputation: 385104
Just creating a JungleMap*
doesn't create a JungleMap
. You formed a pointer, but didn't point it anywhere!
This is particularly dangerous since you then dereference it, and later attempt to delete through it. Yes, this compiles, because a compiler cannot diagnose this in the general case (and is never required to try), but you'll get everything at runtime from silent nothingness, to a crash, to a nuclear explosion.
You are also trying to invoke different functions in two different classes, through changing the type of a pointer (without any inheritance, at that), which is simply not possible and will prevent your code from compiling, even though you've tried to get around it by redeclaring variables locally. I could list a ream of misunderstandings but suffice it to say it's time to read a good introductory C++ book.
I would suggest a combination of inheritance and dynamic allocation, if I knew what you were trying to achieve. A common mistake on SO is to provide nonsense code, then expect us to know what your goal is from that nonsense code; unfortunately we have about as much idea what you really meant to do as the C++ compiler does!
Upvotes: 2