我对 C++ 类的多态性的理解是,它允许以相同的方式处理一个类及其子类。因此,如果我有一个类及其子类的多个对象,并将它们存储在基类的(智能)指针 vector 中,我可以对它们调用任何虚方法,它会工作得很好。
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);
}
最佳答案
这就是您刚刚尝试用“舞者”示例做的事情吗?
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()
。如果舞者是基础 - 舞蹈基础,如果他只是假装 - 舞蹈高级。
关于c++ - 同时处理基类的多个指针时如何处理多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45661357/