C++ 父子类的用法

Posted

技术标签:

【中文标题】C++ 父子类的用法【英文标题】:C++ Parent and Child class usage 【发布时间】:2016-07-04 21:55:42 【问题描述】:

假设我有一个Person 课程。这个人类持有Shape

Shape 父类有几个从它派生的子类。例如RectangleCircle。这些子类中的每一个都有自己的方法。例如,Circle 类有GetRadius(),而 Rectangle 类有GetWidth() 以及更多特定于这种形状的方法。

现在假设我有几个人,其中一些人持有矩形,其他人持有圆形。我想知道每个人都拿着什么形状,我想从这些形状中获取信息。但是我不能这样做,因为 Person 持有一个 Shape,所以它不能访问任何子特定的方法。

我读过一些关于铸造的东西,但我发现它有点令人困惑,我不确定铸造是否是最好的方法,或者是否有更有效的方法来完全解决这个问题。那我该怎么做呢?

编辑 1:编辑以获得更多说明。 我希望GetWidth()GetRadius() 等方法返回不同的类型。

【问题讨论】:

作为您想要用来解决这些问题的快速指针。您需要使用动态绑定。在 C++ 中,这是通过指针和 virtual 关键字实现的。在这种情况下,您必须在每个子类中调用提供信息GetRadius()GetWidth() 相同名称的函数,例如getShapeInfo()getShapeInfo() 也将在 Shape 中定义。您可能会将getShapeInfo() 设为纯虚函数。含义 Shape 是一个抽象类。这是因为,世界上没有形状,只有形状类型的东西,比如圆形。 所以Shape 中的'virtual double getShapeInfo()` 和每个Person 都会有一个Shape *s 以允许动态绑定,然后当CircleRect 也被指向时调用s->getShapeInfo() 将调用圆形或矩形函数,具体取决于s 所指向的对象的类型 @izaak_pyzaak 如果它们返回不同的类型怎么办?来自CirclegetShapeInfo() 将返回双倍半径,但是我希望来自RectanglegetShapeInfo() 返回类似于vec2 的东西,包含宽度和高度。 您可以定义一个新类型ShapeInfo,它从两者都返回。虽然,这在某种程度上将反射问题转移到了一种新类型上,以使函数调用看起来更整洁。但是你不知道谁会保持什么样的形状,这可能是要走的路。你读到的关于铸造的东西是关于向下铸造的危险吗?最终,如果你有两个不同的函数并在一个矩形上调用getRadius,那么坏事就会接踵而至。 @JohnCake 您有一些答案应该可以回答您的大部分问题。尤其是Sam Varshavchik provided那一个。 【参考方案1】:

Shape 超类至少有一个虚拟方法,您当然可以使用dynamic_cast 来确定超类的每个实例是哪个子类。这当然是可能的,有时这是正确的做法,但大多数时候并非如此。

相反,正确的做法是提前设计你的类,以这样一种方式,任何操作都可以用任何Shape 完成,它有一个Shape 方法,通常是虚拟的。最低的共同点是拥有一个由每个子类实现的纯虚方法,它返回一个特定子类的标识符。

但是,一般来说,如果您需要知道超类的实例是什么特定的子类,则说明类层次结构设计不正确。确实,生活并非一直都是 100% 完美的。有时为了处理棘手的情况,可能有必要诉诸这种丑陋的东西。但这不应该是一开始的计划。您应该尝试正确设计您的类层次结构,这样就没有必要了。

即使是最小的公分母:

A) 上述函数返回某个枚举标识符,该标识符对应于超类是哪个特定子类,并且

B) 超类定义了所有超类可以实现的所有可能的虚方法,默认实现会引发异常

可以以不涉及丑陋演员的方式做到这一点。

【讨论】:

您能否给我一段示例代码,说明您将如何设计我提出的这种情况?既然我已经看到了 James Adkison 发布的代码,那么您给出的选项 B 显然是我可以做到的一种方式,但是如果我错了,请纠正我,您的帖子说我应该更好地设计类层次结构而不是那样做。但我自己真的想不出来。 A) 和 B) 不是相互排斥的,我的意思是指出一种使用它们的潜在方法:指示超类是哪个子类的实例,以及子类的可访问方法虚方法。而且,是的,最好重新考虑一个人的阶级层次结构。【参考方案2】:

如果您有一个Shape 类型的指针,其中包含RectangleCircle 等子类型的对象,那么您可以使用dynamic_cast 来检查对象的类型在运行时。 dynamic_cast 将允许您安全地检查其地址由指针保存的对象的运行时类型。

请查看https://en.wikipedia.org/wiki/Run-time_type_information#dynamic_cast 了解如何完成此操作的示例。

【讨论】:

【参考方案3】:

您可以通过在Shape 类上为适用于所有派生类的任何函数声明virtual 函数来避免强制转换。在这种情况下,您想知道每个派生类型是什么,以便您可以拥有一个getType() 函数。您不必完全按照这种方式进行操作,但以下是您可以如何操作的示例。

示例代码

#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Shape

public:
    virtual ~Shape() 

    virtual Shape* clone() const = 0;

    virtual std::string getType() const = 0;
;

class Circle : public Shape

public:
    virtual Circle* clone() const override  return new Circle(*this); 

    virtual std::string getType() const override  return "Circle"; 
;

class Rectangle : public Shape

public:
    virtual Rectangle* clone() const override  return new Rectangle(*this); 
    virtual std::string getType() const override  return "Rectangle"; 
;

class Person

public:
    explicit Person(const std::string& name, const Shape& shape) :
        mName(name),
        mShape(shape.clone())
    
    

    std::string mName;
    std::unique_ptr<Shape> mShape;
;

int main()

    std::vector<Person> people;
    people.emplace_back("Foo", Circle());
    people.emplace_back("Bar", Rectangle());

    for (const auto& person : people)
    
        std::cout << person.mName << " with " << person.mShape->getType() << "\n";
    

    return 0;

示例输出

Foo with Circle
Bar with Rectangle

Live Example

【讨论】:

以上是关于C++ 父子类的用法的主要内容,如果未能解决你的问题,请参考以下文章

继承的概念和意义

子类继承父类,子类执行的时候,父子类的执行顺序?

C++多态的基本概念

react的父子组件执行顺序

java父子类的初始化顺序--个人总结

java的父类和子类的关系都有哪些?