访问存储在vector <base *>中的已知派生类对象时使用dynamic_cast或static_cast有什么问题?

时间:2015-08-11 05:40:32

标签: c++ inheritance vector dynamic-cast static-cast

这是我的问题的后续跟进: Updating data members of different derived classes of the same base class within a vector

我正在使用C ++构建一个3D网格模型,它具有不同的单元格类型,所有这些都存储在 Grid 类中的向量中。我已经定义了一个基本 GridCell 类,我还有几个派生类 GridCell1 GridCell2 GridCell3 ,等等上。

在模型流程中,我传递了一个 GridCell 对象。传递的 GridCell 对象类型(派生类)将与接收模型进程一起使用。换句话说,模型过程模型过程被设计(可能不正确),具有不同的功能,期望在适当的派生对象上起作用。数据成员。当然,他们无法看到,因为对象是从std::vector<gridCell*> gridCellVector中提取的,因此它只将对象视为指向基类的指针。此时,我有两个选择:1)重写我的模型进程,2)使用static_cast或dynamic_cast来向下传播传递的对象。

问题:在这种情况下,当我的模型进程(只在正确类型的 GridCell 对象上运行)知道他们接收的派生类时,我应该使用static_cast还是dynamic_cast?人们似乎对使用这些工具感到非常不满。我为什么要远离使用这些?

2 个答案:

答案 0 :(得分:3)

有时,施法是不可避免的,因为多态性并不能解决所有问题。然而,多态性实际上是为了消除知道类型的需要。如果您需要知道类型,那么您必须询问多态是否是正确的工具,否则如果您未能正确实现多态性。

理想是每个对象都知道自己的行为,因此在处理通用类型时,没有其他人需要做出if/else决定。

为此,您通常会将处理代码放在类型本身中,而不是需要为他们收到的每个对象做出if/else决定的外部处理例程。

因此,虽然 ,但 它是必要的,但如果你需要,它也可能可能设计不良的症状用它。

  

问题:在这种情况下,当我的模型进程(仅在正确类型的 GridCell 对象上运行)知道派生的内容时   他们正在接收的课程,我应该使用static_cast还是dynamic_cast?   人们似乎对使用这些工具感到非常不满。我为什么要   远离使用这些?

如果确实知道该类型,请使用static_cast(更快),否则请使用dynamic_cast

替代投射:

一个&#34;技巧&#34;你可以使用 polymorphism 为你选择正确的类型。

您可以做的是将虚拟函数添加到您的基类GridCell,该基类接受分别处理子类型的类的对象并获取重写的子类型自己调用相关的处理函数。这样,类型本身知道需要如何处理。

示例

class Processor
{
public:

    void process_1(class GridCell1* cell)
    {
        std::cout << "processing type 1\n";
    }

    void process_2(class GridCell2* cell)
    {
        std::cout << "processing type 2\n";
    }
};

class GridCell
{
public:
    virtual ~GridCell() {}

    virtual void process(Processor& proc) = 0;
};

class GridCell1
: public GridCell
{
public:
    void process(Processor& proc) override
    {
        proc.process_1(this);
    }
};

class GridCell2
: public GridCell
{
public:
    void process(Processor& proc) override
    {
        proc.process_2(this);
    }
};

class Grid
{
    std::vector<GridCell*> gridCellVector;

public:
    Grid()
    {
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell2);
        gridCellVector.push_back(new GridCell1);
        gridCellVector.push_back(new GridCell2);
        gridCellVector.push_back(new GridCell1);
    }

    ~Grid() { for(auto cell: gridCellVector) delete cell; }

    void process()
    {
        Processor proc;
        for(auto cell: gridCellVector)
            cell->process(proc);
    }
};

int main ()
{
    Grid grid;

    grid.process();
}

<强>输出:

processing type 1
processing type 1
processing type 2
processing type 1
processing type 2
processing type 1

答案 1 :(得分:1)

使用static_cast&lt;&gt;在描述的情况下是不安全的,因为所描述的演员是一个向下的未知派生类,并且为什么不使用static_cast的最合适的解释是:

  

如果new_type是某个类D的指针或引用,并且表达式的类型是指向其非虚基B的指针或引用,则static_cast执行向下转换。这样的static_cast不进行运行时检查以确保对象的运行时类型实际上是D,并且只有在通过其他方式(例如实现静态多态性)保证此前提条件时才可以安全地使用它。可以使用dynamic_cast进行安全预测。   (Read more about static_cast conversion)

如果不同的对象(gridCell1,gridCell2)具有不同的新函数,比如f1()和f2(),它们不会作为虚函数出现在基类中 - 它是不可能的使用c ++的多态机制,因为编译器只在编译时看到基础对象函数,不能访问派生类的函数。

但是,您可以添加一种机制来帮助您在运行时使用RTTI确定对象类型。通过使基类抽象,您将为每个派生类创建一个vtbl,并且可以尝试对每个派生对象执行dynamic_cast。如果转换失败,它将返回null并继续尝试其他派生类,直到成功,并且您可以使用所需的函数。

示例:

class Base
{
    public:
    virtual void f() = 0;;  
};

class DerivedA : public Base
{
    public:
    virtual void f() 
    {
        cout << "DerivedA::f()";    
    }
    void f1()
    {
        cout << "DerivedA::f1()";
    }
};

class DerivedB : public Base
{
    public:
    virtual void f() 
    {
        cout << "DerivedB::f()";    
    }
    void f2()
    {
        cout << "DerivedB::f2()";
    }
};

void foo(Base* baseObj)
{
    DerivedA* a = dynamic_cast<DerivedA*>(baseObj);
    if (a)
    {
        a->f1();
        return;
    }
    DerivedB* b = dynamic_cast<DerivedB*>(baseObj);
    if (b)
    {
        b->f2();
        return;
    }
}

int main() {
    DerivedB* bObj = new DerivedB();
    foo(bObj); //will print DerivedB::f2()
    return 0;
}