如何在C++中将类(不是对象)作为参数传递
How to pass a class (not an object) as a parameter in C++
简介:大约一个月前,我开始在大学学习C++。这是针对作业的。我们现在瞪大了眼睛,没有掌握很多先进的概念。
博士:假设你有一个Book
。Book
是Pages*
的动态数组。每个Page
可以是WrittenPage
或DrawnPage
。如果要打印所有Pages
,请使用virtual method
.如果您只想打印DrawnPages
或WrittenPages
则必须在Book
内进行某种过滤。怎么做?现在我发现你需要typeid
或某种其他手段来比较每个Page
的subtype
。如果您急于快速简单地查看已接受的答案,请按@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;
}
}
}
但我不确定:
- 如果模板方法正确
- 我怎么称呼它
希望我已经说清楚了,提前非常感谢您的时间。
我可能会避免使用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++土地的任何好公民一样;我把这当作一个练习。
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 如何将对象数组作为参数传递给模板
- 如何将成员函数作为参数传递并在派生对象上执行方法列表
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 对象作为参数传递,就好像我们正在传递构造函数值一样
- 初始化对象以在 C++08 中作为参数传递的首选语法是什么?
- 将对象作为参数传递,没有可行的重载"="
- 在将匿名对象作为参数传递时,不会调用任何构造函数
- 是否可以创建一个指向对象的智能指针并将此智能指针作为参数传递给构造函数?
- C++:将对象作为参数传递
- 将 std::ofstream 对象作为参数传递给类方法
- 将对象向量的指针作为参数传递,但差异更大
- 使用 lambda 和可变参数模板延迟初始化对象 - 任意传递值
- 当数组对象以函数参数传递时,为什么复制构造函数会自称
- 如何将对象作为参数传递
- 将对象作为C 中的参数传递
- 将新对象的名称作为参数传递
- 将对象数组作为参数传递的最佳方法是什么
- 为什么将临时对象作为参数传递需要 std::move?
- 通过引用将对象参数传递给函子