c++ - 如何根据参数的类型实例化不同的类?

标签 c++ oop inheritance

我有一个名为 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 的类型调用不同的打印方法。这是否可能(以及如何)?


编辑

读完评论后,发现主要有两种可能的设计:

  1. 创建一个类 Action,并调用特定方法(print(Type1 *) 或 print(Type2 *))。我认为这是评论中建议的访问者模式。

  2. 创建两个类,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/

相关文章:

c++ - 第一次尝试创建链表

c++ - C++中的指针数组

javascript - 为什么私有(private)/ protected 成员必须来自同一类才能实现类型兼容性?

python - python中的字段继承访问

java - 将 HashMap 与子类一起使用

java - 扩展类不能继承方法吗?

c++ - 避免前向重新声明。这是一个好习惯吗?有必要吗?

C++ 成员函数与自由函数

php - PDO::FETCH_ASSOC 返回 false

c# - 是否可以为不同的类型创建一个通用的抽象父类?