【问题标题】:How to handle polymorphism when dealing simultaneously with multiples pointers of the base class?同时处理基类的多个指针时如何处理多态性?
【发布时间】:2017-08-13 14:21:58
【问题描述】:

我理解 c++ 类的多态性的方式是,它允许以相同的方式处理一个类及其子类。因此,如果我有一个类及其子类的多个对象,并将它们存储在基类的(智能)指针向量中,我可以在它们上调用任何虚拟方法,它会正常工作。

class Dancer {
public:
    virtual void f() const { std::cout << "I am a basic dancer" << std::endl; }
};

class SkilledDancer : public Dancer {
public:
    void f() const { std::cout << "I am a skilled dancer" << std::endl; }
};

int main(int argc, char *argv[])
{
    std::vector< std::shared_ptr<Dancer> > dancers;
    dancers.push_back(std::make_shared<Dancer>(Dancer()));
    dancers.push_back(std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(SkilledDancer())));

    for(auto & dancer : dancers ){
        dancer->f(); //works fine
    }
}

但是当我想要这种行为时,当我使用处理两个对象的运算符或方法时,我遇到了一个问题。如果一个函数接受基类的两个参数的输入,我如何考虑它们实际上可能是派生类的对象这一事实?

class Dancer {
public:
    virtual void g( const Dancer & d ) const {  std::cout << "Let's do a basic dance" << std::endl;  }
};

class SkilledDancer : public Dancer {
public:

    //what should I do here ?

    void g( const Dancer & d ) const {  std::cout << "Let's do an advanced dance" << std::endl;  }
    //would overload Dancer::g but wrong because d is only a Dancer

    void g( const SkilledDancer & d ) {  std::cout << "Let's do an advanced dance" << std::endl;  }
    //doest not overload Dancer::g because different signature
    //so would never be called if dealing with two smart pointers of the base class
};

int main(int argc, char *argv[])
{
    auto basic = std::make_shared<Dancer>(Dancer());
    auto advanced = std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(SkilledDancer()));
    basic->g(*advanced);    //OK
    advanced->g(*advanced); //seems good ...
    advanced->g(*basic);    //... but wrong
}

我设法找到了一种解决方法(代码如下),但它需要派生类的一个额外成员,两个额外的函数调用,并且该函数不能再是 const 了。所以我想知道是否有更好的方法来处理这个问题。

如果我遇到 XY 问题,我的实际问题是关于矩阵的。我想要一个矩阵运算符来乘以矩阵,但是在处理两个特定矩阵(如三角形或对称矩阵,它们是基矩阵类的子类)时,我想调用另一个运算符。请注意,所有类型的矩阵都存储为矩阵基类的指针。

class Dancer {
public:
    virtual void g( const std::shared_ptr<Dancer> & d ) {  std::cout << "Let's do a basic dance" << std::endl;  }
};

class SkilledDancer : public Dancer {
public:
    SkilledDancer() : dummy_g(false) {}
    bool dummy_g;

    void g( const std::shared_ptr<Dancer> & d) {
        if( !dummy_g ){
            dummy_g = true;
            d->g(std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(*this)));
        } else {
            dummy_g = false;
            ((SkilledDancer*)d.get())->dummy_g = false;
            std::cout << "Let's do an advanced dance" << std::endl;
        }       
    }
};

int main(int argc, char *argv[])
{
    //all cases work fine
    basic->g(basic);
    advanced->g(advanced);
    basic->g(advanced); 
    advanced->g(basic);
}

【问题讨论】:

  • 查找双重/多重调度。
  • [OT]:您应该从 std::make_shared&lt;Dancer&gt;(Dancer()) 中删除 Dancer()
  • 感谢您告诉我问题背后的概念名称。因此,如果我理解正确,C++ 中还没有对多分派的原生支持(维基百科文章提到了访问者模式的动态转换,但它们似乎不是很好的选择)。好呼吁 make_shared 评论!
  • 对于矩阵继承,可能是个坏主意,见Circle-ellipse_problem
  • 三角形和对称矩阵只是同一事物的数据配置 - 矩阵。矩阵是否对角线、对称、归一化等都是特定矩阵配置的简单可计算属性。这不是需要多态性的问题。

标签: c++ polymorphism operator-overloading overloading


【解决方案1】:

这就是你刚刚尝试用你的“舞者”例子做的吗?

class Dancer
{
public:
    virtual void danceWith( Dancer * dancer ) {
        std::cout << "Let's do a basic dance" << std::endl;
    }
};

class SkilledDancer : public Dancer
{
public:
    void danceWith( Dancer * dancer ) override
    {
        if(auto skilledDancer = dynamic_cast< SkilledDancer*>(dancer)) {
            danceWith(skilledDancer);
        } else {
            Dancer::danceWith(dancer);
        }
    }

    void danceWith( SkilledDancer * dancer ) {
        std::cout << "Let's do an advanced dance" << std::endl;
    }
};

稍后:

Dancer* basic = new Dancer;
SkilledDancer* advanced = new SkilledDancer;
Dancer* pretending = advanced;

basic   ->danceWith(basic);         // -> Let's do a basic dance
advanced->danceWith(advanced);      // -> Let's do an advanced dance
basic   ->danceWith(advanced);      // -> Let's do a basic dance
advanced->danceWith(basic);         // -> Let's do a basic dance
advanced->danceWith(pretending);    // -> Let's do an advanced dance

基本上,我们只是利用编译器总是选择最适合参数的函数重载这一事实。在SkilledDancer 类中:对于所有SkilledDancer* 类型化参数,调用danceWith() 的第二个重载(它取决于编译时间——没有多态转换开销)。同时,所有Dancer* 类型的参数都需要运行时检查,并调用覆盖的danceWith()。执行检查,然后选择要调用的适当版本的danceWith()。如果舞者是基础 - 舞蹈基础,如果他只是假装 - 舞蹈高级。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-05
    相关资源
    最近更新 更多