关于C ++中的接口类用法

时间:2014-10-16 08:53:03

标签: c++ interface polymorphism abstract-class

我对使用C ++中的接口类有疑问,但不知道它的名称以及如何搜索它。希望你能帮助我,亲切。

我只想用一个简单的例子来讲述我的问题。

我有5种不同的可能对象,例如三角形,正方形,矩形,五角形和六边形

所有这些对象都有共同的属性,所以我将有一个接口类 Shape

现在,我想要的是:我将拥有一个Shape类对象,并希望能够将其用作其他5个对象之一,因为在运行时选择它。

所以我做了类似下面的事情:

class Shape 
{
public:

    virtual int getArea()=0;

    virtual void setWidth(int w)
    {
        width = w;
    }

    virtual void setHeight(int h)
    {
        height = h;
    }


protected:
    int width;
    int height;
};



class Triangle: public Shape
{
public:

    int getArea()
    { 
        return (m_Width * m_Height)/2; 
    }

};

class Rectangle: public Shape
{
public:

    int getArea()
    { 
        return (m_Width * m_Height); 
    }

};

使用时,我想只创建 Shape 的对象,并使用其中一个派生类对其进行初始化。所以,从那时起,我希望它的行为就像这个对象的实例一样:

void main(){

 Shape* shape;

 std::string shapeType;

 std::cout<<"Enter Shape Type: triangle or rectangle."<<std::endl;

 std::cin>>shapeType;


 if (shapeType == "triangle")
     shape = new Triangle();
 else if (shapeType == "rectangle")
     shape = new Rectangle();


 shape->setWidth(5);
 shape->setHeight(7);

 std::cout<<shape->getArea()<<std::endl;


}

到此为止没问题。问题从这里开始。这些派生类可能具有不同的属性,方法。当我将这些方法vs添加到他们自己的类时, shape 对象无法访问它(正确)。可以使用的另一种方法是将新的派生对象转换为 shape 对象,如:

Triangle* triangle = (Triangle*)shape;
// now I can access own attributes of Triangle object.

但是你可能不这么认为这不是一个好方法。除此之外,我只知道一种方法迫使我将所有这些属性写入 Shape 类并在需要的派生类中实现它们,如果不需要其他类,则将其实现为空。

你对这个问题有什么好的解决方案吗?我确信会有,但我对这个科目没有多少经验,所以希望你有一个符合我想要的解决方案。

提前致谢。

6 个答案:

答案 0 :(得分:2)

首先,您的Shape不是接口,而是基本实现类。将它们分开。

Interface是一个抽象类,只有纯虚方法和虚析构函数:

struct IShape
{
    virtual int getArea() =0;
    virtual void setWidth(int w) =0;
    virtual void setHeight(int h) =0;
    // without virtual destructor you cannot
    // properly destroy object through IShape pointer
    virtual ~IShape() {}
};

现在让我们写基础实现:

class CShapeBaseImpl : public IShape
{
public:
    void setWidth(int w) override { width_ = w; }
    void setHeight(int h) override { height_ = h; }
protected:
    int width_ = 0;
    int height_ = 0;
};

在大多数情况下,您希望创建一些具体对象,并仅通过它的界面来处理它。通常是通过abstract factory pattern

完成的
std::unique_ptr<IShape> CreateTriangle( int a, int b, int c )
{
    auto triangle = std::make_unique<Triangle>(a,b,c);
    // there you can work with it as Triangle
    return triangle;
}

CreateTriangle返回后,最好忘记它是Triangle。它现在只有IShape

答案 1 :(得分:1)

如果您的类型为Shape,则表示您打算将其用作Shape。如果您打算使用其中一个特定子类,则使用该特定类型。

如果您仍然希望拥有存储的泛型类型但仍想使用某些特定操作,那么您需要将指针向下转换为带有static_cast的子类指针,但您需要知道哪种类型存储在那里或使用dynamic_cast,在这两种情况下都会闻起来像是糟糕的设计。

您应该能够使用虚拟功能执行所有多态操作,或者根据情况和需求,您可以使用其他设计模式,例如double dispatch

答案 2 :(得分:1)

所有动物都在吠叫吗?

考虑像

这样的层次结构
-> animal
   -> dog 
      -> great_dane       
      -> chiwawa
   -> fish
      -> tuna
      -> shark

有些人认为,如果你有animal* - s的集合并且你想让他们吠叫,animal必须声明一个抽象的virtual void bark()=0来适当地实施great_danechiwawa(第一个cout << "BARK",第二个cout << "bark"),而fish只会将其作为“无所作为””。

如果你想知道某些东西是什么吠叫,那就问问听吧。没有声音?它没有。

还有人(包括我自己)认为这样的方式将导致animal宣布 zoo 的所有经文(包括那些尚未存在的物种)从而使动物类成为一种“上帝对象”(整个宇宙的最终知识所有者),但并非如此。

对于这些人,bark()属于dog,并且要求动物吠叫基本上没有意义,除非你在此之前检查它实际上是一只狗。 在那之后,它将适当地咆哮它的种族。

要检查动物是否是狗,给定animal* myanimal,您只需转换为dog* mydog = dynamic_cast<dog*>(myanimal)

  • mydog是否为空?对不起:那不是狗:不要这样对待它。坏事可能发生。
  • 不是吗?好的:现在你已经拥有了所有“ dog功能” 一目了然。忘掉fish - es

如果你觉得演员太丑陋和频繁,你可以在animal中实现一个辅助函数:

template<class T>
T* animal::as_a() { return dynamic_cast<T*>(this); }

这样你就可以拥有

auto mydog = myanimal->as_a<dog>();
if(mydog) mydog->bark();

现在,将动物变成形状,将狗变成长方形,将鱼变成三角形!

答案 3 :(得分:0)

如果属性与派生的ONLY相关,但与基础无关,则无意在基础中实现它。例如。属性isEquilateral()应该在Triangle类中实现,但不能在Shape基类中实现。

所以你可以做的是使用在每个派生类中重新实现的Type()属性,给出派生对象类型(三角形,矩形等......)

然后静态施法:

Triangle *triangle = static_cast<Triangle*>(shape);

这样的事情:

if (shape->Type() == Shape::Triangle){
    Triangle *triangle = static_cast<Triangle*>(shape);
    bool eq = triangle->isEquilateral();
    ...
}

或者简单地说:

 Triangle *triangle = static_cast<Triangle*>(shape);
 if (triangle){
     bool eq = triangle->isEquilateral();
     ...
 }

但我更喜欢第一种方法。

答案 4 :(得分:0)

我相信您需要使用指向基类的指针来存储Object。因此,您使用Shape*来存储new Triangle(),并且您应该能够访问必需的函数和变量。这是基于记住如何在矢量中执行此操作,详细更好here,但我不明白为什么它不会传输。

Shape* shape;
shape = new Triangle();
//shape->foo accesses Triangle methods

它要么是,要么使用动态/静态强制转换在运行时将其设置为正确的类型,尽管这意味着您需要检查它是什么类型才能进行正确的转换(所以可能有一个识别由每个子类设置的基类中的字符串或其他内容)

答案 5 :(得分:-3)

这绝对不会像你解释的那样起作用。它不是遗产的感觉。你应该读一下heraditing。基本上是它的以下规则:如果你创建一个下属你可以使用父亲的funcs和属性,如果他们是公共的或受保护的