父类引用子变量

时间:2016-12-21 21:43:42

标签: c++ inheritance

我有几个类似的类继承自相同的Base-Class / Interface(Base class 1),它们共享几个类似的函数,但是它们也有自己独特的函数。它们都有自己不同类的成员变量,每个变量都来自相同的Base-Class / Interface(Base class 2)。是否可以在基类1中定义基类2类型的变量,然后在使用基类1的类的实际实现中,使基类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()有效,但在CircleFunc中,m_var仍然是ShapeExtra的类型,因此< strong>不知道CircleExtraFunc存在。我每次想要这样做时都可以施放m_var,但这是重复的,在现实世界的情况下不值得。有没有办法在基于ShapeExtra的唯一类中使用函数,同时将GetVar()函数保留在Shape

如果我遗漏了任何内容,请提问。

3 个答案:

答案 0 :(得分:1)

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

#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;
}

Test it on ideone

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

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

答案 1 :(得分:1)

简单地使用继承而不使用指针是不可能的,因为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)相关,具体形状就可以从扩展模板中受益。你可以通过Shape接口透明地调用方法。

答案 2 :(得分:1)

我建议采用以下方法:

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的引用。这将允许在基类中使用此额外内容。

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