4

介绍性说明:大约一个月前,我开始在大学学习 C++。这是为了一个任务。我们现在只是盯着看,并没有掌握很多先进的概念。

tl;博士:假设你有一个Book. 是的Book动态数组Pages*。每个都Page可以是 aWrittenPage或 a DrawnPage。如果要打印所有内容,Pages请使用virtual method. 如果您只想打印DrawnPages或者WrittenPages您必须在Book. 怎么做?现在我发现你需要typeid或其他某种方式来比较subtype每个Page. 如果您急于做一些快速简单的事情,请查看@CantChooseUsernames 接受的答案。它对我的问题很有效。如果您有更多的专业知识,我想听听您对@nm 的新答案的看法。如果您认为当前接受的答案为讨论带来了新的和有意义的东西,请不要让当前接受的答案阻止您发表评论或发布自己的答案。


原始问题:

我有一个 MyObj 类,它是 TheseObj 和 ThatObj 的超类。

Class TheseObj : public MyObj {
}

Class ThoseObj : public MyObj {
}

我有另一个类,它包含一个带有指向 MyObj 实例的指针的 std::vector 和一个我只想列出这些对象的非静态方法:

Class MyClass {
    private:
    vector<MyObj*> vec;

    public:
    void listTheseObj() {
        for each (myObj* obj in vec) {
            if(typeid(*obj) == typeid(theseObj)) {
                cout << *obj << endl;
            }
        }
    }
}

所有运算符都已正确重载。

这很好用。现在的问题是我有更多的地方需要做同样的事情,所以我需要一个可以接收 GENERIC 向量和类 TYPE 的模板方法,以便我执行以下操作:

listObjects(class_name_here, vec);

我设法创建:

template <class T>
void listObjectsOfOneType(const type_info& class_name_here, const vector<T*>& vec) {
    for each (T* obj in vec) {
        if(typeid(*obj) == typeid(class_name_here)) {
            cout << *obj << endl;
        }
    }
}

但我不确定:

  1. 如果模板方法正确
  2. 我怎么称呼它

希望我已经说清楚了,非常感谢您抽出宝贵的时间。

4

3 回答 3

4

我可能会避免使用 TypeID .. 虽然,我不确定你想要实现什么,但这是我相信你所要求的:

#include <iostream>
#include <vector>
#include <typeinfo>

template <class T, class U>
void ListObjects(std::vector<U*> &vec)
{
    for (U* obj : vec)
    {
        if (typeid(*obj) == typeid(T))
        {
            obj->Print();
            std::cout<<"\n";
        }
    }
}

class Parent
{
    public:
        Parent() {std::cout<<"Parent Constructed\n";}
        virtual ~Parent() {std::cout<<"Parent Destructed\n";}

        virtual void Print(){std::cout<<"Parent\n";}
};

class Brother : public Parent
{
    public:
        Brother(){std::cout<<"Brother Constructed\n";}
        virtual ~Brother(){std::cout<<"Brother Destructed\n";}
        void Print() override {std::cout<<"Brother\n";}
};

class Sister : public Parent
{
    public:
        Sister(){std::cout<<"Sister Constructed\n";}
        virtual ~Sister(){std::cout<<"Sister Destructed\n";}
        void Print() override {std::cout<<"Sister\n";}
};

int main()
{
    std::vector<Parent*> Objects;
    Objects.push_back(new Parent());
    Objects.push_back(new Brother());
    Objects.push_back(new Sister());
    std::cout<<"\n";

    ListObjects<Parent>(Objects);
    ListObjects<Brother>(Objects);
    ListObjects<Sister>(Objects);

    for (Parent* c : Objects)
    {
        delete c;
    }
}

哪个打印:

  • 父构造
  • 父构造
  • 兄弟建造
  • 父构造
  • 修女建造

  • 家长

  • 兄弟
  • 姐姐

  • 父级销毁

  • 兄弟毁灭
  • 父级销毁
  • 姐姐毁了
  • 父级销毁

  • 进程返回 0 (0x0) 执行时间:0.066 s

  • 按任意键继续。

很多评论告诉您不要使用 TypeID,因为我们不确定您想要什么。但是,假设我们知道您想要什么,我们所说的“不需要 typeid”是什么意思,那么以下内容将是有效的:

#include <iostream>
#include <vector>
#include <typeinfo>

template <class T>
void ListObjects(std::vector<T*> &vec)
{
    for (T* obj : vec)
    {
        //TypeID isn't needed here because the virtual call will figure out which class's << operator to call.

        //If each class has a print function, it can also figure out which class's print function to call..
        //obj->Print(); //works too because each class has a print func.
        std::cout<<*obj<<"\n"; //Works because each class has an overloaded << operator.
    }
}

class Parent
{
    protected:
        virtual void Print(std::ostream& os) const {os<<"Parent\n";}

    public:
        Parent() {std::cout<<"Parent Constructed\n";}
        virtual ~Parent() {std::cout<<"Parent Destructed\n";}

        friend std::ostream& operator << (std::ostream &os, const Parent &p);
};

std::ostream& operator << (std::ostream &os, const Parent &p)
{
    p.Print(os);
    return os;
}

class Brother : public Parent
{
    protected:
        void Print(std::ostream& os) const override {os<<"Brother\n";}

    public:
        Brother(){std::cout<<"Brother Constructed\n";}

        virtual ~Brother() {std::cout<<"Brother Destructed\n";}
};

class Sister : public Parent
{
    protected:
        void Print(std::ostream& os) const override {os<<"Sister\n";}

    public:
        Sister(){std::cout<<"Sister Constructed\n";}
        virtual ~Sister(){std::cout<<"Sister Destructed\n";}
};

int main()
{
    std::vector<Parent*> Objects;
    Objects.push_back(new Parent());
    Objects.push_back(new Brother());
    Objects.push_back(new Sister());
    std::cout<<"\n";

    ListObjects(Objects); //NOTICE we all template types are now inferred.

    for (Parent* c : Objects)
    {
        delete c;
    }
}

请注意,由于调用是虚拟的,因此代码打印与使用 TypeID 的代码相同,并且代码不再需要您在模板的大括号中键入任何内容。推断是因为我们不再需要使用 typeid 进行比较。


现在,既然您请求了以前的代码,而是将模板作为参数,那么:

template <class T, class U>
void ListObjects(std::vector<U*> &vec)
{
    for (U* obj : vec)
    {
        if (typeid(*obj) == typeid(T))
        {
            obj->Print();
            std::cout<<"\n";
        }
    }
}

会成为:

template<typename T>
void ListObjects(std::vector<T*> &vec, const std::type_info &type)
{
    for (T* obj : vec)
    {
        if (typeid(*obj) == type)
        {
            std::cout<<*obj<<"\n";
        }
    }
}

你会像这样使用它:ListObjects(Objects, typeid(Child));

同样,所有这些都会为您提供完全相同的结果。这完全取决于您的需求/用例。我们并不完全知道您想要实现的“什么”。这些应该可以帮助你。

于 2013-10-27T05:09:24.640 回答
2

除非您将其作为测试的一部分只是为了弄清楚代码中发生了什么,否则我同意评论者的观点,即这是一个非常糟糕的主意。

template < typename T >
void listObjects(const std::vector<MyObj*>& vec) {
    for (MyObj* obj: vec) {
        if (typeid(*obj) == typeid(T)) {
            // one of the two below, depending on what your op<< looks like
            std::cout << *obj << std::endl;
            std::cout << dynamic_cast<T&>(*obj) << std::endl;
        }
    }
}

void calledLikeThis(const std::vector<MyObj*>& vec) {
    listObjects<TheseObj>(vec);
}
于 2013-10-27T05:18:57.927 回答
0

typeid这种方式使用违反了 Liskov 替换原则。LSP,粗略地说,如果你的函数适用于 X 类的对象,它也应该适用于 X 的任何子类的(一些)对象。你的listTheseObj函数只会列出完全属于 type 的对象TheseObj,而不是任何子类型的对象。

这对于调试目的或基础设施/框架项目来说是可以的,您可以在其中实现反射或序列化等服务并使用typeid(obj). 但是业务逻辑不应该那样工作。TheseObj用户对任何技术原因使您分成几个子类型不感兴趣;他们想要他们的类型概念(如果有的话)。

如果您只想打印任何子类类型的对象,您可以TheseObj 替换

typeid(*obj) == typeid(TheseObj)

dynamic_cast<TheseObj*>(obj) != 0

模板版本如下所示:

template<typename T, typename U>
void ListObjects(std::vector<T*> &vec>)
{
    for (T* obj : vec)
    {
        if (dynamic_cast<U*>(obj) != 0)
        {
            std::cout<<*obj<<"\n";
        }
    }
}

值得注意的是,身体if不以任何方式使用条件。这暗示了将它们分开的可能性。

template<typename T>
void ActOnObjects(std::vector<T*> &vec>, std::function<bool(T*)> predicate,
                                         std::function<void(T*)> action)
{
    for (T* obj : vec)
    {
        if (predicate(obj))
        {
            action(obj);
        }
    }
}

现在您可以使用任何谓词进行过滤,无论是否使用 RTTI。

ActOnObjects(objects, [](T* obj){return dynamic_cast<ThatObj*>(obj) != 0;},
                      [](T* obj){std::cout << *obj << std::endl;});

ActOnObjects(objects, [](T* obj){return obj->isVeryImportant();},
                      [](T* obj){std::cout << *obj << std::endl;});

ActOnObjects(objects, [](T* obj){return obj->isBlue() && obj->isWobbly();},
                      [](T* obj){std::cout << *obj << std::endl;});

也请使用迭代器范围而不是容器,就像 C++ 领域的任何好公民一样;我把它留作练习。

于 2013-10-27T20:15:50.913 回答