使用抽象类的继承和多态

时间:2012-06-20 08:17:28

标签: c++ inheritance

我有一个抽象类型A,以及两个派生类型A1A2
我想在类A中添加一个方法M,它采用类型A的参数。但是,我需要ad hoc多态。

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

我可以在课程A中放置所有签名声明,但它很糟糕。

4 个答案:

答案 0 :(得分:2)

使用simulated double dispatch

class A {
public:
    virtual void M(A &) = 0;
    virtual void M(A1 &) = 0;
    virtual void M(A2 &) = 0;
};

class A1 : public A {
public:
    virtual void M(A &a) { a.M(*this); }
    virtual void M(A1 &a) { std::cout << "A1 <- A1\n"; }
    virtual void M(A2 &a) { std::cout << "A2 <- A1\n"; }
};

class A2 : public A {
public:
    virtual void M(A &a) { a.M(*this); }
    virtual void M(A1 &a) { std::cout << "A1 <- A2\n"; }
    virtual void M(A2 &a) { std::cout << "A2 <- A2\n"; }
};

(参见例如http://ideone.com/nycls。)

我认为没有办法避免基类中的多次重载。

答案 1 :(得分:0)

如果你想要多态行为 - 那么你需要的只是基类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;}
};

然后:

A1 * a1 = new A1();
a1->M(a1); //prints "A1"
A  * a = a1;
a->M(a1); //prints "A"

我认为这不是你想要的行为

答案 2 :(得分:0)

为什么不做那样的事?

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." );
    }
}

为A2 :: M做等效的事情吗?

答案 3 :(得分:0)

这是双重调度。当你写:

A* p1;
A* p2;
p1->M(*p2);

应根据*p1的类型和*p2的类型进行调度。

在开始之前,您必须意识到这意味着n^2的功能 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,因为它不是 可复制。 C ++ 11提供了type_index来包装它;如果你正在使用 较旧的编译器,自己实现它是相当简单的。该 基本原则是(可能在A本身)的行为:

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 );
}

真正的问题在于编写每个单独的函数和 在地图中插入地址(首次使用前)。功能 当然,他们自己可以使用dynamic_cast,甚至是static_cast 你可以肯定他们只能从这里打电话,他们可以 所涉及的班级的朋友,但仍有 n 2 他们。 (一个常见的解决方案是制作它们 其中一个类的静态成员,并拥有每个派生类 定义一个类型的静态成员,它执行所有的注册 它负责的职能。)