Introdutory note 我大约一个月前开始在大学学习C ++。这是一项任务。我们现在正在盯着看,并没有掌握许多先进的概念。
tl; dr:让我们假设你有Book
。 Book
是Pages*
的动态数组。每个Page
可以是WrittenPage
或DrawnPage
。如果您要打印所有Pages
,请使用virtual method
。如果您只想打印DrawnPages
或WrittenPages
,则必须在Book
内进行某种过滤。怎么做?现在我发现你需要typeid
或某种其他方法来比较每个subtype
的{{1}}。如果你急于快速简单地看看@CantChooseUsernames接受的答案。它对我的问题很有用。如果你有更多的专业知识,我想听听你对@n.m的新答案的看法。 。如果您认为它为讨论带来了新的和有意义的东西,请不要让当前接受的答案阻止您评论或发布自己的答案。
原始问题:
我有一个类MyObj,它是TheseObj和TheseObj的超类。
Page
我有另一个类,它包含一个带有指向MyObj实例的指针的std :: vector和一个非静态方法,其中我只想列出TheseObj:
Class TheseObj : public MyObj {
}
Class ThoseObj : public MyObj {
}
所有操作员都已正确重载。
这很有效。现在的问题是我有更多的地方需要做同样的事情,所以我需要一个可以接收GENERIC向量和类TYPE的模板方法,以便我做类似的事情: / p>
Class MyClass {
private:
vector<MyObj*> vec;
public:
void listTheseObj() {
for each (myObj* obj in vec) {
if(typeid(*obj) == typeid(theseObj)) {
cout << *obj << endl;
}
}
}
}
我设法创建:
listObjects(class_name_here, vec);
但我不确定:
希望我已经说清楚了,非常感谢你的时间。
答案 0 :(得分: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;
}
}
打印哪些:
Sister Constructed
父
姊妹
父母被破坏
父母被破坏
进程返回0(0x0)执行时间:0.066秒
很多评论告诉你不要使用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));
同样,所有这些都会给你完全相同的结果。这一切都取决于您的需求/用例。我们并不完全知道你想要达到的“什么”。这些应该会帮助你。
答案 1 :(得分: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);
}
答案 2 :(得分:0)
以这种方式使用typeid
违反了Liskov Substitution原则。粗略地讲,LSP说如果你的函数与X类的对象一起工作,它也应该与X的任何子类的(某些)对象一起工作。你的listTheseObj
函数只列出的对象< / em>类型为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 ++的好公民一样;我将此作为练习。