C++ 父子类的用法
Posted
技术标签:
【中文标题】C++ 父子类的用法【英文标题】:C++ Parent and Child class usage 【发布时间】:2016-07-04 21:55:42 【问题描述】:假设我有一个Person
课程。这个人类持有Shape
。
Shape
父类有几个从它派生的子类。例如Rectangle
和Circle
。这些子类中的每一个都有自己的方法。例如,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
以允许动态绑定,然后当Circle
或Rect
也被指向时调用s->getShapeInfo()
将调用圆形或矩形函数,具体取决于s
所指向的对象的类型
@izaak_pyzaak 如果它们返回不同的类型怎么办?来自Circle
的getShapeInfo()
将返回双倍半径,但是我希望来自Rectangle
的getShapeInfo()
返回类似于vec2
的东西,包含宽度和高度。
您可以定义一个新类型ShapeInfo
,它从两者都返回。虽然,这在某种程度上将反射问题转移到了一种新类型上,以使函数调用看起来更整洁。但是你不知道谁会保持什么样的形状,这可能是要走的路。你读到的关于铸造的东西是关于向下铸造的危险吗?最终,如果你有两个不同的函数并在一个矩形上调用getRadius
,那么坏事就会接踵而至。
@JohnCake 您有一些答案应该可以回答您的大部分问题。尤其是Sam Varshavchik provided那一个。
【参考方案1】:
Shape
超类至少有一个虚拟方法,您当然可以使用dynamic_cast
来确定超类的每个实例是哪个子类。这当然是可能的,有时这是正确的做法,但大多数时候并非如此。
相反,正确的做法是提前设计你的类,以这样一种方式,任何操作都可以用任何Shape
完成,它有一个Shape
方法,通常是虚拟的。最低的共同点是拥有一个由每个子类实现的纯虚方法,它返回一个特定子类的标识符。
但是,一般来说,如果您需要知道超类的实例是什么特定的子类,则说明类层次结构设计不正确。确实,生活并非一直都是 100% 完美的。有时为了处理棘手的情况,可能有必要诉诸这种丑陋的东西。但这不应该是一开始的计划。您应该尝试正确设计您的类层次结构,这样就没有必要了。
即使是最小的公分母:
A) 上述函数返回某个枚举标识符,该标识符对应于超类是哪个特定子类,并且
B) 超类定义了所有超类可以实现的所有可能的虚方法,默认实现会引发异常
可以以不涉及丑陋演员的方式做到这一点。
【讨论】:
您能否给我一段示例代码,说明您将如何设计我提出的这种情况?既然我已经看到了 James Adkison 发布的代码,那么您给出的选项 B 显然是我可以做到的一种方式,但是如果我错了,请纠正我,您的帖子说我应该更好地设计类层次结构而不是那样做。但我自己真的想不出来。 A) 和 B) 不是相互排斥的,我的意思是指出一种使用它们的潜在方法:指示超类是哪个子类的实例,以及子类的可访问方法虚方法。而且,是的,最好重新考虑一个人的阶级层次结构。【参考方案2】:如果您有一个Shape
类型的指针,其中包含Rectangle
或Circle
等子类型的对象,那么您可以使用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++ 父子类的用法的主要内容,如果未能解决你的问题,请参考以下文章