我有一个名为 Type 的虚拟类,以及一个派生类 Type1 和 Type2。
class Type {
public:
virtual void print() = 0;
};
class Type1 : public Type {
public:
void print() { cout << "I am of type 1" << endl; }
};
class Type2 : public Type {
public:
void print() { cout << "I am of type 2" << endl; }
};
根据用户输入的参数,我将实例化一个类或另一个类。
然后我想要另一个类来生成操作,具体取决于作为参数给出的“Type”对象的类型。目前,此类是:
class Action {
protected:
Type *t;
public:
Action(Type *t) : t(t) {} ;
void print() {
cout << "I am an action. My type says: ";
t->print();
}
};
如何让 Action::print 根据属性“t”的类型生成不同的任务?
我尝试过的:
- 创建两个派生类,即 Action1 和 Action2,并在实例化 Type1(分别是 Action2 和 Type2)时实例化 Action1。但这正是我不想做的:我希望 Action “自己”知道要做什么。
- 将 Action 定义为模板类(将 Type 替换为模板类型)。但以同样的方式,我必须实例化 Action< Type1> 或 Action< Type2> 类型的对象,这是我不想做的事情。
我的愿望是有一个看起来像这样的主线:
int main(int argc, const char * argv[]) {
Type *t = new Type1();
Action *act = new Action(t);
act->print();
delete act, t;
return 0;
}
但是根据 t 的类型调用不同的打印方法。这是否可能(以及如何)?
编辑
读完评论后,发现主要有两种可能的设计:
创建一个类 Action,并调用特定方法(print(Type1 *) 或 print(Type2 *))。我认为这是评论中建议的访问者模式。
创建两个类,Action1 和 Action2(可能从 Action 派生,也可能不是!),并实例化与 t、Type1 或 Type2 相对应的类。
一种解决方案是否比另一种更干净、更易于维护等?
编辑2
一些评论中提到的工厂模式怎么样?谁能告诉我它是否解决了我的问题?
最佳答案
首先说一点:我个人认为“使用命名空间”是一种不好的做法,因为它会影响头文件。整个互联网都描述了原因。
此外,出于异常安全的考虑,我总是尝试尽量减少使用指针。 Sutter 有一本关于此问题的好书,名为 Exceptional C++,其中详细描述了这些问题。然而,指针显然有其用途,特别是多态性。最后,如果类只有公共(public)成员,我喜欢创建类结构......这只是一个品味问题。
让我们从一些代码开始:
#include <string>
#include <iostream>
#include <memory>
struct Type
{
virtual void print() = 0;
};
struct Type1 : Type
{
void print() { std::cout << "I am of type 1" << std::endl; }
};
struct Type2 : Type
{
void print() { std::cout << "I am of type 2" << std::endl; }
};
class Action
{
protected:
std::unique_ptr<Type> t;
public:
Action(std::unique_ptr<Type> &&t) : t(std::move(t)) {};
void print()
{
std::cout << "I am an action. My type says: ";
t->print();
}
};
int main()
{
Action act(std::make_unique<Type1>());
act.print();
return 0;
}
您似乎要解决的第一个问题是用户输入生成类型。根据输入,这可能指向抽象工厂或构建器模式,甚至是成熟的解析器,但如果这是一个简单的输入决策,最好 KISS:
int main()
{
std::string s;
std::getline(std::cin, s);
std::unique_ptr<Type> type;
if (s == "1")
{
type = std::make_unique<Type1>();
}
else
{
type = std::make_unique<Type2>();
}
Action act(std::move(type));
act.print();
return 0;
}
通常您希望将模型与实现分开。操作有多种不同的形状和形式,因此您可能想要根据您的模型做其他事情。访问者模式、双重调度或者任何你想称之为的东西都可以在这里发挥作用。
请注意,我通常使用稍微修改过的访问者,它返回基本类型作为默认值。这样,您可以轻松地将转换合并到代码中,恕我直言,这是常见的设计要求。此时我将只使用指针。就我个人而言,我非常喜欢使用内存领域来解决内存管理问题,但我认为这超出了本答案的范围。
最后,一个简单的基类可以帮助您摆脱无意义的管道。
struct TypeVisitor;
struct Type
{
virtual Type* Accept(TypeVisitor& visitor) = 0;
};
template <typename T>
struct TypeBase : Type
{
virtual Type* Accept(TypeVisitor& visitor) override
{
return visitor.Handle(static_cast<T*>(this));
}
};
struct Type1 : TypeBase<Type1>
{
};
struct Type2 : TypeBase<Type2>
{
};
struct TypeVisitor
{
virtual Type* Handle(Type1* input)
{
/* if necessary, recurse here like: input->child = input->child->Accept(this); */
return input;
}
virtual Type* Handle(Type2* input) { return input; }
};
struct DumpAction : TypeVisitor
{
virtual Type* Handle(Type1* input) override
{
std::cout << "Handling type 1." << std::endl;
return TypeVisitor::Handle(input);
}
virtual Type* Handle(Type2* input) override
{
std::cout << "Handling type 2." << std::endl;
return TypeVisitor::Handle(input);
}
};
int main()
{
DumpAction act;
Type2 type2;
type2.Accept(act);
return 0;
}
关于c++ - 如何根据参数的类型实例化不同的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43362744/