数组中对象的C++多态性

Meh*_*ide 12 c++ arrays polymorphism

我是一名嵌入式软件工程师,来自比特和C世界.在那个世界中,闪存中的数据由C表示为const.并且RAM中有数据.RAM昂贵且有限,而闪存便宜且足够.此外,由于碎片问题或安全规定,不允许使用new,delete,malloc等进行动态内存分配,因此首选静态设计.

我有大约2000个具有相似常量属性但行为不同的对象.所以对于他们来说,我将Shape Class定义为一个基类,它保存了我的对象的共享属性.为了表示不同的行为,Shape Class有一个名为Print()的抽象方法,它将被父母覆盖.

ShapeList是重要的部分.它是一个const数组,由"const Shapes"组成,因此它们将被链接器放入闪存部分.

以下程序产生一个输出:

I'm a Shape has 3 dots
I'm a Shape has 4 dots
I'm a Shape has 5 dots
Run Code Online (Sandbox Code Playgroud)

虽然预期产量是:

I'm a Triangle has 3 dots
I'm a Rectangle has 4 dots
I'm a Pentagon has 5 dots
Run Code Online (Sandbox Code Playgroud)

我需要多态行为.当我打印三角形时,它应该像三角形,而不是形状.我怎样才能做到这一点?

谢谢.

#include <array>
#include <cstdio>
class Shape
{
    public:
    const int DotCount;
    Shape(const int dot): DotCount(dot) {}
    virtual void Print(void) const; // this is virtual method
};

void Shape::Print(void) const
{
    printf("I'm a Shape has %d dots\n", DotCount);
}

class Triangle: public Shape
{
    public:
    Triangle(void): Shape(3) { }
    void Print(void) const;
};

void Triangle::Print(void) const
{
    printf("I'm a Triangle has %d dots\n", DotCount);
}

class Rectangle: public Shape
{
    public:
    Rectangle(void): Shape(4) { }
    void Print(void) const;
};

void Rectangle::Print(void) const
{
    printf("I'm a Rectangle has %d dots\n", DotCount);
}

class Pentagon: public Shape
{
    public:
    Pentagon(void): Shape(5) { }
    void Print(void) const;
};

void Pentagon::Print(void) const
{
    printf("I'm a Pentagon has %d dots\n", DotCount);
}

const std::array<const Shape, 3> ShapeList = { Triangle(), Rectangle(), Pentagon() };

int main(void)
{
    ShapeList.at(0).Print();
    ShapeList.at(1).Print();
    ShapeList.at(2).Print();
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

更多问题:今天我意识到虚拟功能还有另一个问题.当我将任何虚函数添加到基类中时,编译器开始忽略"const"指令并将对象自动放入RAM而不是闪存.我不知道为什么.我已经向IAR提出了这个问题.到目前为止我得到的结论是,即使使用堆,ROMable类也不能实现多态行为:/

小智 10

此版本不使用动态内存:

Triangle tri;
Rectangle rect;
Pentagon pent;
const std::array<const Shape*, 3> ShapeList {
    &tri, &rect, &pent
};
for (unsigned int i = 0; i < ShapeList.size(); i++)
    ShapeList[i]->Print();
Run Code Online (Sandbox Code Playgroud)

在像C#这样的语言中,您可以使用as关键字来实现"多态".在C++中,它看起来像这样:

    const Triangle* tri = dynamic_cast<const Triangle*>(ShapeList[i]);
    if (tri)
        static_cast<Triangle>(*tri).SomethingSpecial();
Run Code Online (Sandbox Code Playgroud)

如果返回的指针dynamic_cast有效,则可以调用Triangle特殊函数.例如,这将允许您有一个循环迭代ShapeList并仅调用Triangle方法.如果您可以使用异常,请考虑将其包装在一个try catch块中并捕获std::bad_cast.

注意:你需要一个const指针因为ShapeList[i]是const.static_cast必要的原因是因为你在const指针上调用非const方法.你可以添加const限定符,SomethingSpecial() const然后就可以了tri->SomethingSpecial().否则,你只是抛弃了const.

例如:

static_cast<Triangle*>(tri)->SomethingSpecial();
// error: static_cast from type 'const Triangle*' to type 'Triangle*' 
// casts away qualifiers
Run Code Online (Sandbox Code Playgroud)

这将有效:

const_cast<Triangle*>(tri)->SomethingSpecial();
Run Code Online (Sandbox Code Playgroud)


小智 10

正如其他人所指出的那样,方便和通用的方式并不像那样.修复它会导致代码与目标平台的限制不一致.但是,您可以通过多种不同方式模拟多态.

您可以按类型隔离对象:

const Triangle tris[] = {tri1, tri2, ...};
const Rectangle rects[] = {rect1, rect2, ...};
// for correct order, if needed
const Shape * const shapes[] = {&tris[0], &rects[2], &rects[0], ...}:
Run Code Online (Sandbox Code Playgroud)

你仍然需要让所有的方法(不同行为的各类)virtual,以及(如果算上虚函数表的指针,这将是一个有点不公平二)每个对象你付出额外的指针.您还可以删除所有virtual有利于显式标记的内容:

enum ShapeKind { Triangle, Rectangle, Pentagon };
struct Shape {
    ShapeKind kind;
    int sides;
    ...
};
Run Code Online (Sandbox Code Playgroud)

使用union如果不同子类中需要非常不同的会员数据.这有很多严格的限制,导致相当丑陋的代码,但可以很好地工作.例如,您需要预先了解您的层次结构,并且子类需要大致相同的大小.请注意,这不一定比virtual替代方法更快,但是当它适用时,它可以占用更少的空间(一个字节而不是一个vtable指针)并使内省更精简.


Mik*_*olf 9

您可以使用多态性以及所有约束,只需对代码进行微小更改:

const Triangle triangle;
const Rectangle rectangle;
const Pentagon pentagon;

const std::array<const Shape*, 3> ShapeList = { &triangle, &rectangle, &pentagon };
Run Code Online (Sandbox Code Playgroud)


Jes*_*ing 5

任何简单的修复都是添加一个字符串形状,定义它是什么类型的形状.

class Shape
{
    public:
    const int DotCount;
    const char* shapeType
    Shape(const int dot, const char* type): DotCount(dot), shapeType(type) {}
    void Print(void) const;
};

void Shape::Print(void) const
{
    printf("I'm a "); printf(shapeType); printf(" has %d dots\n", DotCount);
}

class Triangle: public Shape
{
    public:
    Triangle(void): Shape(3, "Triangle") { }
};
Run Code Online (Sandbox Code Playgroud)