C++ 使用抽象类的继承和多态性

C++ 使用抽象类的继承和多态性,c++,inheritance,C++,Inheritance,我有一个抽象类型A,还有两个派生类型A1和A2 我想向类a添加一个方法M,该方法采用类型a的参数。但是,我需要特殊多态性 实际上,我需要3个实现:A1::M(a1a),A1::M(a2a)和A2:(a1a),A2::M(a2a)。 但是我想要一种抽象的方法,用类型为A的指针调用方法M 我可以将所有签名声明放在类A中,但它很糟糕 如果您想要多态行为,那么您所需要的只是基类A中的一个方法。然后您可以在A1、A2中重新实现该方法 然后你可以写: A *a1 = new A1(); A *a2 = ne

我有一个抽象类型
A
,还有两个派生类型
A1
A2

我想向类a添加一个方法M,该方法采用类型a的参数。但是,我需要特殊多态性

实际上,我需要3个实现:
A1::M(a1a)
A1::M(a2a)
A2:(a1a)
A2::M(a2a)
。 但是我想要一种抽象的方法,用类型为A的指针调用方法M


我可以将所有签名声明放在类
A
中,但它很糟糕

如果您想要多态行为,那么您所需要的只是基类A中的一个方法。然后您可以在A1、A2中重新实现该方法

然后你可以写:

A *a1 = new A1();
A *a2 = new A2();

a1->M(a2); //polymorphic behavior
如果你做了这样的东西:

struct A
{
    virtual void M(A *a) {}
};

struct A1 : public A
{
    virtual void M(A1 *a) {cout << "A1" << endl;}
    virtual void M(A *a) {cout << "A" << endl;}
};

我认为这不是你想要的行为,为什么不做那样的事呢

void A1::M( A a )
{
    if( dynamic_cast< A1* >( &a ) )
    {
        // do your A1::M1(A1 a) stuff
    }
    else
    if( dynamic_cast< A2* >( &a ) )
    {
        // do your A1::M2(A2 a) stuff
    }
    else
    {
        throw std::logic_error( "Unsupported A type." );
    }
}
void A1::M(A)
{
if(动态施法(&a))
{
//做你的A1::M1(A1 a)的事情
}
其他的
if(动态投影(&a))
{
//做你的A1::M2(A2 a)的东西
}
其他的
{
抛出std::logic_错误(“不支持的类型”);
}
}
对于A2::M?

使用,与do等效

A类{
公众:
虚空M(A&)=0;
虚空M(A1&)=0;
虚空M(A2&)=0;
};
A1类:公共A{
公众:
虚空M(A&A){A.M(*this);}

虚空M(A1&a){std::cout这是双重分派。当您写入:

A* p1;
A* p2;
p1->M(*p2);
应在
*p1
类型和
*p2
类型上进行调度

在开始之前,您必须意识到这意味着
n
不同的派生类型。在某个地方,一定有人知道 所有派生类型(除非您可以定义某种类型的 未知类型对的“默认”实现)

有两种实现方法。最简单的方法是 是关闭的(即客户端代码不能引入新的派生类) 通常在基类中使用大量虚拟函数 受保护,因为它们不是为在外部调用而设计的 层次结构:

//  Forward references needed for all derived classes...
class A1;
class A2;
//  ...

class A
{
protectd:
    virtual void doM(A1* arg) = 0;
    virtual void doM(A2* arg) = 0;
    //  ...

public:
    virtual void M(A& arg) = 0;
};
在派生类中,
M
的实现总是相同的:

void A1::M(A& arg)
{
    arg.doM( this );
}
这很简单,而且相对有效,但需要在 抽象基和所有派生类(必须实现 新的虚拟函数)每次添加新的派生类时,它都是 但是,对于封闭层次结构很有用;我在使用 他们行为的一部分的策略模式,其中 策略都是在源文件中定义的,并且没有公开给 客户(战略的抽象基础只是向前的) 声明,因此如果添加了 战略)

一个更通用的解决方案将涉及一个
std::map
,带有一对
typeid
作为索引。不能直接使用
typeid
,因为它不是索引 copyable.C++11提供了一个
类型索引来包装它;如果您使用的是
较旧的编译器,自己实现一个相当简单
基本原则大致如下(可能是A本身):

真正的问题在于编写每个单独的函数和 在地图中插入地址(首次使用前)。函数 当然,他们自己也可以使用
动态强制转换
,甚至
静态强制转换
,如果需要的话 你可以确定他们只会在这里被调用,而且他们可以被调用 涉及的班级朋友,但仍有 一个常见的解决办法是使它们 其中一个类的静态成员,并具有每个派生类 定义一个类型的静态成员,该成员注册所有
它所负责的函数。)

如果你想通过
A
类型的指针来实现这一点,你没有很多选择,只能将
virtualm(A1)
virtualm(A2)
放在基类中。并且不能以任何方式利用(即,让双重分派发生,子成员提供实际实施)?我不是绝对想要使用类型a的指针,我想要一个抽象接口来访问objetcs A1和A2,但我不知道没有类型a的指针怎么办。访问者模式似乎很好。我也想要参数中的多态行为,我不知道如何重新实现基类a中声明的一个虚拟方法,因为我不能reim使用另一个签名的plement方法你是对的,但我可能不会使用dynamic_cast。而且,即使它可以根据需要工作,我认为它是一个糟糕的设计。无论如何,谢谢,如果我找不到任何其他方法,我会这样做。这是一个经典的反模式(但我认为在某些特殊情况下它是合适的).你说得对,@JamesKanze,这一点都不好。但它会起作用。事实上,我认为这是最好的解决方案。谢谢你的长篇大论。层次结构是封闭的,只有两个不同的子类型,我想我可以使用你的第一个方法,而不会带来不便。我可以提供一个默认实现,尽管它没有效率:combinat在特定情况下,ory用于加速计算。添加更多子类型只是内部重构的问题,即使修改所有子类并不理想。
//  Forward references needed for all derived classes...
class A1;
class A2;
//  ...

class A
{
protectd:
    virtual void doM(A1* arg) = 0;
    virtual void doM(A2* arg) = 0;
    //  ...

public:
    virtual void M(A& arg) = 0;
};
void A1::M(A& arg)
{
    arg.doM( this );
}
typedef std::pair<std::type_index, std::type_index> TypePairKey;
typedef void (*FuncPtr)( M* arg1, M* arg2 );
typedef std::unordered_map<TypePairKey, FuncPtr> DispatchMap;

static DispatchMap ourDispatchMap;
void M( A& arg )        //  NOT virtual !!!
{
    DispatchMap::iterator entry
        = ourDispatchMap.find(
                DispatchMap::value_type( typeid( *this ), typeid( arg ) ) );
    assert( entry != ourDispatchMap.end() );
        //  Or some default handling, maybe throw NotYetImplemented()
    (*entry->second)( this, &arg );
}