将类的指针从一个类更改为另一个类

时间:2014-01-13 06:27:05

标签: c++ class pointers delete-operator

我对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.

我如何实现这一目标?这真的让我烦恼,特别是因为它编译没有问题。

4 个答案:

答案 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)

你应该做的是坐下来思考你想要解决的问题,并做出正确的设计。在您的情况下,您有两个“位置”,“玩家”应该能够在这些位置之间移动。从那开始我们确定了两个可能的类(LocationPlayer)和一个行为(玩家可以从一个位置移动到另一个位置)。

根据以上信息,您可以执行以下操作:

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的细节移动到专门的函数中。但我认为这超出了回复范围。