【问题标题】:C++ List of Base Classes and How to Determine Class TypeC++ 基类列表以及如何确定类类型
【发布时间】:2012-03-01 03:50:28
【问题描述】:

我有一个基类line,它有一个子类arc(请注意,其他类将从line继承)...我需要存储一个列表,或者vectorline对象可能是linearcarc 具有line 没有的附加属性。因此,当我处理我的列表或line 对象中的vector 时,如何确定该对象是line 还是arc

我准备了一个小示例程序,并在 cmets 中放入了我将尝试完成的示例的伪代码。这甚至可能吗?

#include <iostream>
#include <vector>
#include <iterator>

using namespace std;

struct point
{
    double x;
    double y;
};

class line
{
    public:

        point start;
        point end;

};

class arc: public line
{
    public:

        point center;

};


int main( int argc, char* argv[] )
{


    vector<line*> a;
    a.push_back( new line );
    a.push_back( new arc );

    for( vector<line*>::iterator i = a.begin(); i < a.end(); i++ )
    {
        (*i)->start.x = 10;
        (*i)->start.y = 11;
        (*i)->end.x = 101;
        (*i)->end.y = 102;
        //if( type of (*i) is arc ) 
        //{
        //  (i)->center.x = 111;
        //  (i)->center.y = 112;
        // }
    }

    return 0;

}

【问题讨论】:

    标签: c++ class vector


    【解决方案1】:

    在你的循环中,试试这个:

    arc *ar = dynamic_cast<arc *>(*i);
    if ( NULL != ar ) {
     // it's an arc!
    }else {
      // it's just a line
    }
    

    Sid 也是对的……使用虚函数代替。在这种简单的情况下使用 dynamic_cast 通常被认为是糟糕的风格。

    【讨论】:

    • arc 将具有各种功能,例如getRadius(),不适用于line,所以在这个项目上使用虚函数对我来说毫无意义。我已经看到与您在此处发布的示例类似的示例,但是当我尝试这样做时出现此编译器错误...cannot dynamic_cast ‘i.__gnu_cxx::__normal_iterator&lt;_Iterator, _Container&gt;::operator* [with _Iterator = line**, _Container = std::vector&lt;line*&gt;, __gnu_cxx::__normal_iterator&lt;_Iterator, _Container&gt;::reference = line*&amp;]()’ (of type ‘class line*’) to type ‘class arc*’ (source type is not polymorphic)
    • 啊,是的,缺少的 vtable。好吧...您可以通过在类行中使(空)析构函数为虚拟来添加一个。不是最好的方法,但它会起作用。
    • 那么,回到 Sid 的观点,您为什么需要将它们保存在容器存放线上*?如果你这里没有任何虚函数,那似乎不太合适。
    • 我通过向基类添加至少一个虚函数来让您的示例工作。你说这是一种不好的做法,但除非我想用它真的不需要的虚拟成员来膨胀我的基类,否则我看不到除此之外的任何其他选项......
    • 因为我需要这些类型的对象的列表,并且每个单独的项目可以是 linearc 或其他类型的 line,我还没有添加。此外,它们将被写入/读取到纯文本文件/从纯文本文件中读取,因此我需要在遍历它们时确定它们的真正类型,以便可以在文件中指出。
    【解决方案2】:
    class curve
    {
    public:
        typedef void * type;
    
    public:
        virtual type rtti() const = 0;
    };
    
    #define     
    DEFINE_RTTI  \
        public: \
            virtual type rtti() const { return desc(); }        \
        public: \
            inline static type desc() { return &desc; } \
    
    class line : public curve
    {
        DEFINE_RTTI;
    };
    
    class arc : public curve
    {
        DEFINE_RTTI;
    };
    
    ///////////////////////////////////////////////////////////////////////////////
    //  Main program
    int main()
    {
        arc arc_;
        line line_;
    
        curve *curve_ = new arc();
    
        _ASSERT(arc_.rtti() == arc::desc());
        _ASSERT(arc_.rtti() != line::desc());
    
        _ASSERT(line_.rtti() != arc::desc());
        _ASSERT(line_.rtti() == line::desc());
    
        return 0;
    }
    

    这个 rtti 在一个模块(exe 或 dll)中工作,如果你想在多个模块中使用它,你必须构建类字典

    【讨论】:

      【解决方案3】:

      为什么需要确定?如果你绝对需要,你可以通过 RTTI 使用 typeid 字段。但是通常如果您使用虚拟方法,如果您的程序设计良好,您不需要知道类是什么。在基类引用或指针上调用虚函数将根据它所指向的内容调用适当的实例方法。 这就是通过类层次结构实现多态性的全部意义所在。

      但同样,如果您绝对需要知道实例属于哪个类,请使用 typeid。

      此外,您不能通过基类指针访问派生类成员数据。您只能通过基类接口进行交谈。这就是抽象的全部意义所在。 一种丑陋的解决方法是创建一个胖接口,在基类接口中为这些数据成员提供访问器/修改器作为虚拟成员函数。

      【讨论】:

      • 除了main 函数之外,我的示例中绝对没有任何函数,更不用说虚拟函数了。我尝试使用typeid() 函数来确定类类型,但是,我所有的努力都导致编译器错误,或者它返回了指针类型而不是实际类。最后,这并没有解决能够访问未在基类中定义的对象成员的问题......
      • 您确定在您的编译器中启用了 rtti。我认为必须为他们中的很多人提供选项--rtti-enabled 或类似的选项。查看我的编辑。
      • 是的,rtii 在我目前使用的 g++ 中默认启用。您向我介绍了一些我不熟悉的新术语(访问器、突变器)。你有这样的例子吗?
      猜你喜欢
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多