簡體   English   中英

使用抽象類的繼承和多態

[英]Inheritance and polymorphism using abstract class

我有一個抽象類型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類中,但它很糟糕。

使用模擬雙重調度

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 。)

我不認為有一種方法可以避免基類中的多次重載。

如果你想要多態行為 - 那么你需要的只是基類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"

我不認為這是你想要的行為

為什么不做那樣的事情呢?

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等效嗎?

這是雙重調度。 當你寫:

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

真正的問題在於編寫每個單獨的函數並在地圖中插入它們的地址(在第一次使用之前)。 of them. 當然,函數本身可以使用dynamic_cast ,甚至是static_cast ,如果你可以確定它們只能從這里調用,並且它們可以是所涉及的類的朋友,但是仍然有個。 (一種常見的解決方案是使它們成為其中一個類的靜態成員,並使每個派生類定義一個類型的靜態成員,該成員執行其負責的所有函數的注冊。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM