【问题标题】:Unable to access subclass methods from baseclass pointer无法从基类指针访问子类方法
【发布时间】:2018-08-01 20:45:30
【问题描述】:

不确定我的继承在哪里出错,但我似乎只能在将子类实例存储在基类指针中时访问基类的方法:

class Car
{
    public:
        Car():type("Car") {};
        Car(const char* t):type(t) {};
        void get_type() const { return this->type; };
    private:
        std::string type;
};

class Ford : public Car
{
    public:
        Ford():Car("Ford"), doors(4) {};
        Ford(int x):Car("Ford"), doors(x) {};
        void get_doors() const { return this->doors; };
    private:
        int doors;
};

int main()
{
    Car* c = nullptr;
    c = new Ford();
    c->get_doors(); // doesn't exist, only allows for get_type()
}

这很可能是指针的滥用。我承认 C++ 不是我的强项,所以我试图复制一个用 Python 编写的程序,它大量使用继承,但它比用 C++ 编写的程序简单得多,因为你没有明确使用指针和引用(在抽象级别)。

【问题讨论】:

  • 您可以在Car 中将get_doors() 设为纯虚函数,并在Ford 中覆盖它。但是它不能返回void
  • “抽象类实例化”是什么意思?你不能实例化一个抽象类。
  • 是的,您可以使用Car* 指向来自Car 的派生类的任何实例。但您也仅限于Car 可用的操作。 (因为不能保证你的Car* 指向的东西支持get_doors。想象一下如果第二行是c = new Car();。)
  • 这就是虚函数的用途,正如我在第一条评论中提到的那样。 C++ 是静态类型的,不像 Python 那样具有“鸭子类型”。
  • 试试static_cast<Ford *>(c)->get_doors();

标签: c++ c++11


【解决方案1】:

Python 方法是动态的 - 在 smalltalk 本质上是纯面向对象的。你向一个实例发送一条消息,如果它可以回复它,它就会回复。

C++ 不是这样的。它使用 static 类型。您已声明一个Car* 类型的对象,因此a) 它可以保存指向任何Car 实例或Car 子类型实例的指针,并且b) 它只能被告知这样做可以告诉Cars 做什么。

C++ 也有 virtual 方法。如果在Car 中声明的方法是虚拟的,那么它可以在Car 的子类中实现(例如,在Ford 中)。然后在通过Car 指针调用时将调用该子类的实现。

【讨论】:

  • 我最终使用了virutal 关键字并阅读了文档,这是有道理的。谢谢!
  • 附录:这样做是因为 C++ 力求让程序只为程序员要求的内容付费。动态调度是有代价的,所以在 C++ 中,你只有在要求时才支付这个价格。
【解决方案2】:

这正是应该发生的事情。 Car* 指针仅允许您查看 Car 成员。尝试使用 Ford* 指针来查看 Ford 方法。要了解原因,请考虑您有另一个派生自 Car 的类:

class Honda : public Car {
    // no get_doors() here
    void honk();
};

... 

Car* c = new Honda();
c->get_doors(); // What should happen here???

另一方面,如果您希望每个 Car 拥有自己的门数,您可以做几件事:

你可以在基类中声明一个常量成员:

class Car {
public:
    Car(int num_doors) : m_doors(num_doors) {}

    int get_doors() const { return m_doors; }
private:
    const int m_doors;
}

class Ford : public Car {
    Ford() : Car(4) {} // Now every ford has 4 doors
}

class Honda : public Car {
    Honda() : Car(5) {} // and every honda has 5
}

你可以做的另一件事是在你的基类中声明一个虚函数,并在每个子类中覆盖它:

class Car {
public:
    virtual int num_doors() const = 0;
}

class Ford : public Car {
    int num_doors() const override { return 4; } // Now every ford has 4 doors
}

class Honda : public Car {
    int num_doors() const override { return 5; } // and every honda has 5
}

【讨论】:

  • 我最终使用了virtual 关键字,但没有使用override。相反,我在Car 中声明了virtual get_doors() const {};,然后在Ford 中声明了virtual get_doors() const {return this->doors;};。我需要使用override吗?
  • @pstatix:我推荐它。使用override,如果你犯了错误,你会得到一个编译错误。没有它,你会得到奇怪的运行时行为。
  • 确实,虽然override 是可选的,但它只能做好事。见stackoverflow.com/questions/18198314/…
猜你喜欢
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多