簡體   English   中英

在設計中避免使用RTTI

[英]Avoiding RTTI in a design

我有一個Visual Studio 2008 C ++應用程序,它具有從同一基礎派生的幾種對象。 例如:

class Base
{
public:
    std::string Time() const { /*return formatted time*/; };
private:
    SYSTEMTIME time_;
};

class Foo : public Base
{
public:
    const char* FooFunc() const { return "Hello from foo!"; };
};

typedef std::vector< Base > BaseList;

class Bar : public Base
{
public:
    const char* BarFunc() const { return "Hello from bar!"; };

    void push_back( const Base& obj ) { list_.push_back( obj ); };
    BaseList::const_iterator begin() const { return list_.begin(); };
    BaseList::const_iterator end() const { return list_.end(); };

private:
    BaseList list_;
};

這些對象存儲在std::vector< Base > 我需要輸出每個FooBar類中的信息以及存儲在基礎中的信息。 但是,我想避免使用RTTI

int main( int, char** )
{
    BaseList list;

    Foo foo;
    Bar bar;
    Foo foo2;

    list.push_back( foo );
    list.push_back( bar );
    bar.push_back( foo2 );

    for( BaseList::const_iterator it = list.begin();
         it != list.end();
         ++it )
    {
        printf( "%s ", it->Time() );

        // print Foo information for objects of type Foo
        // OR print Bar information for objects of type Bar.
        // Descend in to objects of type Bar to print its children.
    }
    return 0;
}

在這種情況下,所需的輸出將是:

11:13:05 Hello from foo!
11:22:14 Hello from bar!
    11:26:04 Hello from foo!

我可以對此設計進行哪些更改,從而避免使用RTTI作為解決方案,但仍然允許我將具有不同功能的對象(如FooBar存儲在嵌套的樹狀結構中?

謝謝PaulH

這是通過繼承的原始運行時多態性。

用從Base繼承的純虛擬方法DoTheOutput實現替換FooFuncBarFunc ,然后在循環中通過Base*調用該方法。 如果您還需要Base輸出,則Base::DoTheOutput將不是純的,並且在子類實現完成派生特定於類的輸出之后將被子類實現調用。

如果需要在接口中保持BarFuncFooFunc完整,可以將它們委托給新的DoTheOutput函數。

首先,Bar類存在問題。 由於您使用的是std::vector< Base > ,因此將對象切片(即對象的Base部分的副本插入到矢量中,而不是對象)。 您將要使用std::vector< Base* > ,它是指針的向量。

其次,要執行所需的操作,可以在Foo和Bar類都覆蓋的Base類中使用虛擬方法。 如果您認為您的應用程序需要多個遍歷操作之一,則可以查看Visitor模式

好的,根據Alf的建議,我正在將我的評論作為答案。

即使RTTI在這里也不起作用,因為您將Base對象存儲在向量中。 這些對象將被切片 ,從而失去派生類的任何信息。 您需要存儲Base指針,最好是智能指針。 此外,您沒有虛擬功能。 RTTI需要至少一個虛擬功能才能工作。

使您的函數成為Base的純虛擬方法,然后以適當的行為在每個派生類中重寫它。 這將使Base成為抽象類,從而使切片問題成為不可能。

聽起來您想使用多態性。 創建一個虛函數並讓子類實現它。 然后使您的列表成為指向基址的指針列表:

class Base
{
public:
    virtual const char *Func() = 0; // pure virtual, no implementation
};

class Foo: public Base
{
public:
    virtual const char *Func() { return "Hello from foo!"; }
};

class Bar: public Base
{
public:
    virtual const char *Func() { return "Hello from bar!"; }
};

int main()
{
    std::list<Base*> myList;
    myList.push_back(new Foo());
    myList.push_back(new Bar());

    for( std::list<Base*>::const_iterator it = myList.begin();
         it != myList.end();
         ++it )
    {
        printf( "%s ", (*it)->Func() );
    }

// don't forget to delete your pointers.  Or better yet, use smart pointers

    return 0;
}

我有一個Visual Studio 2008 C ++應用程序,它具有從同一基礎派生的幾種對象。 ...這些對象存儲在std :: vector <Base>中。 我需要輸出每個Foo和Bar類中的信息以及存儲在基礎中的信息。 但是,我想避免使用RTTI。 ...我可以對此設計進行哪些更改,從而避免使用RTTI作為解決方案,但仍然允許我將具有不同功能的對象(如Foo和Bar)存儲在嵌套的樹狀結構中?

重要說明 :您說使用的是std::vector<Base> ,如果是這樣,則需要更改為std::vector<Base*> (或Boost指針容器),以避免對象切片。

摘要 :您有一個基類和從其派生的類。 您希望從基類派生的類基於它們的實際作用來做事,還希望基類提供對從其派生的所有類有意義的方法。

:適當地重命名FooFuncBarFunc 讓它們覆蓋基類中的virtual函數:

class Base {
public:
    std::string Time() const { /*return formatted time*/; };
    // this is a pure virtual function (the "= 0" part)
    // there are other kinds of virtual functions that would also work
    virtual const char* Func() const = 0;
private:
    SYSTEMTIME time_;
};

class Foo : public Base {
public:
    const char* Func() const { return "Hello from foo!"; };
};

typedef std::vector<Base*> BaseList;

class Bar : public Base
{
public:
    const char* Func() const { return "Hello from bar!"; };

    void push_back( const Base& obj ) { list_.push_back( obj ); };
    BaseList::const_iterator begin() const { return list_.begin(); };
    BaseList::const_iterator end() const { return list_.end(); };

private:
    BaseList list_;
};

int main( int, char** )
{
    BaseList list;

    Foo foo;
    Bar bar;
    Foo foo2;

    // you normally don't want to do it this way, but since the
    // container won't outlive the stack objects you'll be safe.
    list.push_back(&foo);
    list.push_back(&bar);
    bar.push_back(&foo2);

    for(BaseList::const_iterator it = list.begin();
        it != list.end();
        ++it )
    {
        // the "extra" * was added because the list is a vector<Base*>
        // instead of a vector<Base>
        printf("%s ", *it->Time());

        // print Foo information for objects of type Foo
        // OR print Bar information for objects of type Bar.
        printf("%s\n", *it->Func());
        // Descend in to objects of type Bar to print its children.

        // This you'll need to use RTTI to know when you're looking at a
        // Bar object and need to descend into it
        bar* = dynamic_cast<Bar*>(*it);
        if (bar == NULL)
            continue;
        for (BaseList::const_iterator bit = bar->begin(); bit != bar->end(); ++bit)
            printf("\t%s\n", bit->Func());
    }
    return 0;
}

暫無
暫無

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

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