引用子变量的父类

Parent class referencing child variable

本文关键字:父类 变量 引用      更新时间:2023-10-16

我有几个类似的类继承自同一个基类/接口(基类1),它们共享几个相似的函数,但也有自己独特的函数。它们也有自己不同类的成员变量,并且每个成员变量都继承自同一个基类/接口(基类 2)。是否可以在基类 1 中定义一个类型为 Base 类 2 的变量,然后在使用基类 1 的实际实现中,将 Base 类 2 类型的变量作为其正确类型。有点难以解释,所以下面简化了示例。

//Base-Class 1
class Shape
{
public Shape() {}
ShapeExtra m_var;
//The common functions
public GetVar(){ return m_var; }
}
class Circle : Shape
{
public Circle() { m_var = new CircleExtra(); }
public void CircleFunc()
{
m_var.CircleExtraFunc();
}
}
class Triangle : Shape
{
public Triangle() { m_var = new TriangleExtra(); }
public void TriangleFunc()
{
m_var.TriangleExtraFunc();
}
}
.
.
.

//Base_Class 2
class ShapeExtra
{
public ShapeExtra() {}
}
class CircleExtra : ExtraClass
{
public CircleExtra() {}
void CircleExtraFunc() {//Do stuff}
}
class TriangleExtra : ExtraClass
{
public TriangleExtra() {}
void TriangleExtra() {//Do stuff}
}
.
.
.

因此,我需要将子类中的m_var保留为自己的唯一版本。因为现在(没有额外的CircleExtra m_var;),GetVar()工作,但在CircleFuncm_var仍然是ShapeExtra类型,因此不知道CircleExtraFunc存在。每次我想这样做时,我都可以投m_var,但那是重复的,在我的现实世界中不值得。有没有办法利用基于ShapeExtra的唯一类中的函数,同时保持GetVar()函数Shape

如果我遗漏了什么,请提问。

简单地使用继承而不使用指针是不可能的,因为C++是一种静态和严格类型的语言。

可以继承变量和函数,但需要强制转换函数返回值。

您还可以重写函数以使其返回具体类型,但随后必须在函数内强制转换变量。

你也可以在子类中声明与具体类相同的 var,但随后你只是在超类中隐藏变量而不继承任何内容。

我宁愿使用模板寻求解决方案。使变量的类型成为模板类型,并使用子类中的具体类型扩展模板。它会完美地工作。

自从我上次在C++编程以来已经有很长时间了,如果以下示例中存在错误,请原谅。我相信你可以很容易地让它工作。

template <class S>
class Shape {
S m_var;
//......
public:
S var () {
return m_var;
}
//.......
}
class Circle: Shape <CircleExtra> {
// var method returns CircleExtra
//......
}

编辑: 关于一些注释,为了允许虚拟调用该方法,可以使用相关的返回类型。类似于以下示例。

class Shape {
public:
virtual ShapeExtra *var () = 0;
}
template <typename SE>
class ConcreteShape: Shape {
public:
virtual SE *var() {
return &m_var;
}
// Constructor, etc.
private:
SE m_var;
}

或者一些变化。现在,具体形状可以从扩展模板中受益,只要 SE * 与 ShapeExtra * 相关(类型参数扩展 ShapeExtra)。并且您可以通过形状接口透明地使用该方法。

使用指针,这是完全可能的。 使用您的示例,您可以执行以下操作:

#include <iostream>
#include <memory>
using namespace std;
//Extras
class ShapeExtra
{
public:
ShapeExtra() {}
void ShapeFunc() { std::cout << "Shape"; }
virtual ~ShapeExtra() = default; //Important!
};
class Shape
{
public:
std::unique_ptr<ShapeExtra> m_var;
//require a pointer on construction
//make sure to document, that Shape class takes ownership and handles deletion
Shape(ShapeExtra* p):m_var(p){}
//The common functions
ShapeExtra& GetVar(){ return *m_var; }
void ShapeFunc() {m_var->ShapeFunc();}
};

class CircleExtra : public ShapeExtra
{
public:
void CircleExtraFunc() {std::cout << "Circle";}
};
class Circle : public Shape
{
CircleExtra* m_var;
public:
Circle() : Shape(new CircleExtra()) {
m_var = static_cast<CircleExtra*>(Shape::m_var.get()); 
}
void CircleFunc()
{
m_var->CircleExtraFunc();
}
};

int main() {
Circle c;
//use the ShapeExtra Object
c.GetVar().ShapeFunc();
//call via forwarded function
c.ShapeFunc();
//call the circleExtra Function
c.CircleFunc();
return 0;
}

在 ideone 上进行测试

请注意指针和虚拟析构函数的使用:

  • 通过在ShapeExtra基类中使用虚拟析构函数,可以使用ShapeExtra*销毁任何派生类的对象。这很重要,因为
  • 通过使用std::unique_ptr<ShapeExtra>而不是普通的 C 指针,我们确保在销毁Shape时正确删除对象。
  • 记录这种行为可能是一个好主意,即Shape拥有ShapeExtra*的所有权。这尤其意味着,我们不会删除Circle析构函数中的CirleExtra*
  • 我决定在这里要求对构造进行ShapeExtra*,但也可以稍后使用std::unique_ptr::reset()并检查取消引用Shape::m_varnullptr
  • 构造
  • 顺序是这样的:在调用Circle的构造函数时,我们首先创建一个新的CircleExtra,在最后执行Circle的构造函数之前传递给Shape
  • 销毁顺序首先Circle(最后创建),然后Shape,这也为我们破坏了ShapeExtra,包括(通过虚拟功能)CircleExtra

我推荐以下方法:

class ShapeExtra
{
public:
virtual ~ShapeExtra() { }
virtual void SomeCommonShapeFunc() { std::cout << "Shape"; }
};
class Shape
{
public:
virtual ShapeExtra &GetVar() = 0; // Accessor function.
};

请注意,类Shape根本没有任何数据成员。之后,对于每个派生类,您需要:

class CircleExtra : public ShapeExtra
{
public:
void SomeCommonShapeFunc() { std::cout << "Circle"; }
};
class Circle : public Shape
{
CircleExtra m_var;   // Data member with circle specific class.
public:
virtual ShapeExtra &GetVar() { return m_var; }
};

Circle中虚拟方法的实现将返回对基类ShapeExtra的引用。这将允许在基类中使用此额外内容。

请注意,根本不使用指针和模板。这简化了整体设计。