如何在C++中将类(不是对象)作为参数传递

How to pass a class (not an object) as a parameter in C++

本文关键字:对象 参数传递 C++      更新时间:2023-10-16

简介:大约一个月前,我开始在大学学习C++。这是针对作业的。我们现在瞪大了眼睛,没有掌握很多先进的概念。

博士:假设你有一个BookBookPages*的动态数组。每个Page可以是WrittenPageDrawnPage。如果要打印所有Pages,请使用virtual method .如果您只想打印DrawnPagesWrittenPages则必须在Book内进行某种过滤。怎么做?现在我发现你需要typeid或某种其他手段来比较每个Pagesubtype。如果您急于快速简单地查看已接受的答案,请按@CantChooseUsernames查看。它对我的问题很有效。如果你有更多的专业知识,我想听听你对@n.m.的新答案的看法。不要让当前接受的答案阻止您发表评论或发布自己的答案,如果您认为它为讨论带来了新的和有意义的东西。


原始问题:

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

Class TheseObj : public MyObj {
}
Class ThoseObj : public MyObj {
}

我还有另一个类,它包含一个 std::vector,其中包含指向 MyObj 实例的指针和一个非静态方法,我只想在其中列出 TheseObj:

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. 我怎么称呼它

希望我已经说清楚了,提前非常感谢您的时间。

我可能会避免使用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 Constructedn";}
        virtual ~Parent() {std::cout<<"Parent Destructedn";}
        virtual void Print(){std::cout<<"Parentn";}
};
class Brother : public Parent
{
    public:
        Brother(){std::cout<<"Brother Constructedn";}
        virtual ~Brother(){std::cout<<"Brother Destructedn";}
        void Print() override {std::cout<<"Brothern";}
};
class Sister : public Parent
{
    public:
        Sister(){std::cout<<"Sister Constructedn";}
        virtual ~Sister(){std::cout<<"Sister Destructedn";}
        void Print() override {std::cout<<"Sistern";}
};
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<<"Parentn";}
    public:
        Parent() {std::cout<<"Parent Constructedn";}
        virtual ~Parent() {std::cout<<"Parent Destructedn";}
        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<<"Brothern";}
    public:
        Brother(){std::cout<<"Brother Constructedn";}
        virtual ~Brother() {std::cout<<"Brother Destructedn";}
};
class Sister : public Parent
{
    protected:
        void Print(std::ostream& os) const override {os<<"Sistern";}
    public:
        Sister(){std::cout<<"Sister Constructedn";}
        virtual ~Sister(){std::cout<<"Sister Destructedn";}
};
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));

同样,所有这些都会给您完全相同的结果。这完全取决于您的需求/用例。我们并不完全知道您想要实现"什么"。不过,这些应该对您有所帮助。

除非您这样做作为

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

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

以这种方式使用 typeid 违反了 Liskov 替换原则。LSP 粗略地说,如果你的函数适用于 X 类的对象,它也应该与 X 的任何子类的(某些)对象一起工作。您的listTheseObj函数将只列出完全属于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++土地的任何好公民一样;我把这当作一个练习。