我对C ++很陌生,并且无法将指针指向一个类到另一个类。这就是我所拥有的,它编译没有错误,但不能按照我想要的方式工作。
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();
}
这就是输出:
You are going north towards the river.
You are going north towards the river.
这就是我想要的输出:
You are going north towards the river.
You are going north away from the river.
我如何实现这一目标?这真的让我烦恼,特别是因为它编译没有问题。
答案 0 :(得分:2)
仅创建JungleMap*
不会创建JungleMap
。你形成了一个指针,但没有把它指向任何地方!
这是特别危险的,因为你取消引用它,然后尝试通过它删除。是的,这是编译,因为编译器无法在一般情况下诊断这一点(并且永远不需要尝试),但是您将在运行时从静音虚无,崩溃到核爆炸获得所有内容。
你也试图在两个不同的类中调用不同的函数,通过更改指针的类型(没有任何继承),这是根本不可能的,将< / em>防止你的代码编译,即使你试图通过在本地重新声明变量来绕过它。我可以列出一些误解,但只需说它是时候阅读a good introductory C++ book。
如果我知道你想要实现的,我会建议继承和动态分配的组合。 SO上的常见错误是提供无意义的代码,然后希望我们从无意义的代码中知道您的目标是什么;不幸的是,我们对C ++编译器所做的真正意味着什么有了多少想法!
答案 1 :(得分:1)
您可以通过创建JungleMap和RiverMap派生的基类来完成此工作(至少在最小程度上)。然后,您将拥有一个指向基类的指针,您可以指向其中一个派生类的实例。您还需要重新排列代码以使其编译。
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;
}
注意:这里我只想说尽可能接近原始设计,但仍然有一些代码可能至少类工作。我当然不会把它当作一个模范设计,或者甚至接近它(因为,坦率地说,它不是)。
答案 2 :(得分:0)
你应该做的是坐下来思考你想要解决的问题,并做出正确的设计。在您的情况下,您有两个“位置”,“玩家”应该能够在这些位置之间移动。从那开始我们确定了两个可能的类(Location
和Player
)和一个行为(玩家可以从一个位置移动到另一个位置)。
根据以上信息,您可以执行以下操作:
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();
}
在上面的代码中,您有一个玩家对象,它具有“当前位置”。移动播放器时,只需更改该播放器的当前位置即可。播放器的当前位置充当您拥有的全局Map
变量。
注意:我不是说这是一个好的设计或代码,只是它很简单。
但是,如果你真的是C ++的新手,你应该从一些更简单的问题开始,包括关于指针和继承的教程。
答案 3 :(得分:0)
你似乎混淆了宣言和作业。
以下代码行称为声明,它告诉编译器事物的属性和属性。
JungleMap *Map;
在这行代码之后,编译器知道“Map”是一个指向JungleMap的指针的符号(名称)。
编译器不必对声明做任何事情,除非它有副作用,此时它变成一个定义,这意味着声明调用一个非平凡的构造函数或提供一个赋值:
struct Foo {};
struct Baz { Baz() { std::cout << "Baz is here\n"; } };
这些是声明 - 它们不创建对象的实例,它们描述实例的布局和功能。在某些时候,您必须使用定义或调用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"
}
在上面的代码中,我们从不使用“Baz”,我们从不声明Baz类型的对象,因此编译器实际上会抛弃它。因为这么多的变量都是微不足道的并且没有副作用,所以编译上面的结果在功能上等同于我们写的:
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"
}
所有其他人什么也没做。
C ++还允许您重复使用名称,只要它们位于不同的范围内,但这会创建一个新的隐藏(“阴影”)事物:
#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;
}
因此,您编写的代码将被编译,因为它在每个go函数中声明了一个新的私有“Map”,然后根本就不使用它们。
请注意,上面我能够在内部范围内以不同于外部的方式声明i
。
C ++不允许您更改变量的类型 - 在上面的代码中有两个变量,称为i。当我们创建第二个i时,它是第二个变量i ,原始变量没有更改。
为了做你想做的事情,你需要学习“多态”和“继承”,C ++概念可以让你描述一个“房间”或“位置”,然后基础JungleMap和RiverMap在该基本定义上,您可以将指针指向核心概念Room,并编写处理房间的通用代码,同时将Jungle,River或BridgeMap的细节移动到专门的函数中。但我认为这超出了回复范围。