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

C++ 如何根据参数的类型实例化不同的类?,c++,oop,inheritance,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

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


编辑

在阅读了评论之后,有两种主要的可能设计:

  • 创建一个类、操作并调用特定的方法(print(Type1*)或print(Type2*))。我认为这是评论中建议的访客模式

  • 创建两个类,Action1和Action2(可能来自Action,也可能不是!),并实例化与t、Type1或Type2相对应的一个类

  • 一种溶液是否比另一种更清洁、更易于维护等


    编辑2


    在一些评论中提到的工厂模式呢?有人能告诉我它是否能解决我的问题吗

    您的代码工作正常,设计良好。正如评论中所说,“您将功能放在
    Type1
    /
    Type2
    中,并使用多态性来区分实现”,这是一种很好的方法。
    您可以遵循这种方法,在
    Type1
    /
    Type2
    中执行,并以这种方式控制
    操作中的输出

    动态播放

    不管第一点是什么,你都可以通过检查类型来实现你想要的。您要检查
    Action
    的构造函数中的
    t
    是否为
    Type1
    类型或
    Type2
    类型。只需尝试使用
    dynamic\u cast
    向下播放即可

    Action::print()
    的代码可能与此类似:

    Action::print(){
        Type1* t1 = dynamic_cast<Type1*>(t); /* returns nullptr if t is not of type Type1
    
        /* Check if we got the nullptr or not */
        if(t1){
            std::cout << "I am an action. My type says: Type 1!" << std::endl;
        }
        else{
             std::cout << "I am an action. My type says: Type 2!" << std::endl;
        }
    }
    
    输出:

    I am an action. My type says: Type 1!
    I am an action. My type says: Type 2!
    

    请不要说这是一个最小的例子,我认为使用清晰结构化多态性的解决方案是更好的选择。

    虽然基于继承的多态性确实有它的用途,但这听起来不像是其中之一。通常,在基于继承的设计中,执行的任务应该只取决于相关类的行为,而不是它们的具体类型

    绕过继承限制的一种方法是使用访问者模式,它本质上通过类对访问请求的反应来公开继承层次结构中类的具体类型

    有一种情况是,当用户的代码库无法控制时,可能会添加额外的派生类,而这种情况下,您无法使用访问者模式绕过基于继承的设计。但据我所知,这不是一个问题

    因此,另一种方法是通过
    variant
    而不是继承(我在这里使用的是c++17
    std::variant
    s,但您也可以使用Boost.variant,如果您无法访问已经实现
    std::variant
    的编译器/STL),只需将您的类型定义为

    class Type1 {
    public:
        void print() const {
            std::cout << "I am of type 1\n"; 
        }
    };
    
    class Type2 {
    public:
        void print() const { 
            std::cout << "I am of type 2\n"; 
        }
    };
    
    该操作本身如下所示:

    class Action {
        Type t_;
    public:
        Action(Type t)
            : t_(std::move(t)) {
         }
    
         void print() {
             auto action = make_combined(
                 [](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
                 [](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); } 
             );
             std::visit(action, t_);
         }
     };
    
    int main() {
        Type t1 = Type1{}; 
        auto a1 = Action{t1};
        a1.print();
    
        auto a2 = Action{Type2{}};  
        a2.print();
    }
    
    工作示例可在上找到

    如果您以后想添加一个附加类型
    Type3
    ,只需创建它,然后使用声明将它添加到
    type
    中的变量中,编译器就会告诉您需要在哪里为新类型添加新的处理程序

    如果您喜欢
    操作
    类中未明确提及的类型的某种默认行为,您也可以只向
    操作
    类中的组合lambda添加一个通用lambda,如:

    auto action = make_combined(
        [](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
        [](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); },
        [](auto const& t) { std::cout << t.print(); } // this will be selected for all other types 
    );
    
    auto action=make\u组合(
    
    [](Type1 const&t){std::cout使用混合,您可以非常自然地编写类。您只需扩展CRTP模式,使它们能够在以后使用混合技术。代码比任何文本解释都简单,因此请查看您自己:

    #include <iostream>
    #include <memory>
    
    class Type
    {
        public:
            virtual void Do()=0;
    };
    
    template <typename Base>
    class Type1: public Base
    {
        void Do() override { std::cout << "Type1::Do" << std::endl; }
    };
    
    template <typename Base>
    class Type2: public Base
    {
        void Do() override { std::cout << "Type2::Do" << std::endl; }
    };
    
    template <typename Base>
    class Action: public Base
    {
        public:
        virtual void Print()=0;
    };
    
    template <typename Base>
    class Action1: public Base
    {
        void Print() override { std::cout << "Print for Action1" << std::endl;}
    };
    
    template <typename Base>
    class Action2: public Base
    {
        void Print() override { std::cout << "Print for Action2" << std::endl;}
    };
    
    int main()
    {
    
        // what we want "mixin" is Action into Type
        using Base = Action<Type>;
    
        std::unique_ptr<Base> a1 { new Action1<Type1<Base>> };    // Action1 in Type1
        std::unique_ptr<Base> a2 { new Action2<Type2<Base>> };    // Action2 in Type2
    
        a1->Do();
        a1->Print();
    
        a2->Do();
        a2->Print();
    }
    
    #包括
    #包括
    类类型
    {
    公众:
    虚空Do()=0;
    };
    模板
    类别类型1:公共基础
    {
    
    void Do()override{std::cout下面是我的结论

    #include <iostream>
    using namespace std;
    
    /** Class Type and its derived classes Type1 and Type2
     */
    class Type {
    public:
        int type;
        virtual void print() = 0;
    };
    class Type1 : public Type {
    public:
        Type1() { type = 1; }
        void print() { cout << "I am of type 1" << endl; }
    };
    class Type2 : public Type {
    public:
        Type2() { type = 2; }
        void print() { cout << "I am of type 2" << endl; }
    };
    
    /** Class Action and its derived classes Action1 and Action2
     */
    class Action {
    protected:
        Type *t;
    public:
        Action(Type *t) : t(t) {};
        static Action *make_action(Type *t);
        virtual void print() = 0;
    };
    class Action1 : public Action {
    public:
        Action1(Type *t) : Action(t) {};
        void print() {
            cout << "I am an action1. My type says: ";
            t->print();
        }
    };
    class Action2 : public Action {
    public:
        Action2(Type *t) : Action(t) {};
        void print() {
            cout << "I am an action2. My type says: ";
            t->print();
        }
    };
    
    Action *Action::make_action(Type *t) {
        switch(t->type) {
        case 1:
            return new Action1(t);
            break;
        case 2:
            return new Action2(t);
            break;
        }
    }
    
    /** Main
     */
    int main(int argc, const char * argv[]) {
        Type *ta = new Type1();
        Action *acta = Action::make_action(ta);
        acta->print();
        delete acta, ta;
    
        Type *tb = new Type2();
        Action *actb = Action::make_action(tb);
        actb->print();
        delete actb, tb;
    
        return 0;
    }
    

    似乎是预期的工作。如果有人看到错误,请让我知道。

    第一句话:我个人认为“使用命名空间”是一种不好的做法,因为它通过头文件波动。原因是在因特网上描述的。< /P>

    也是,由于异常安全,我总是尽量减少指针的使用。萨特有一本关于它的好书,叫做Excel C++,它详细描述了这些问题。但是指针显然有其用途,特别是多态性。最后,我喜欢制作类结构,如果它们只有公共成员…这只是品味的问题

    让我们从一些代码开始:

    #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;
    }
    
    #包括
    #包括
    #包括
    结构类型
    {
    虚空打印()=0;
    };
    结构类型1:类型
    {
    void print(){std::cout child=input->child->Accept(此)*/
    返回输入;
    }
    虚拟类型*句柄(类型2*输入){返回输入;}
    };
    结构转储操作:TypeVisitor
    {
    虚拟类型*句柄(类型1*输入)重写
    {
    
    std::难道我看不出问题所在吗…在我看来,它正是你想要的,对吗?你把功能放在Type1/Type2中,并使用多态性来区分实现;听起来不错…@atlaste:事实上,我希望动作根据t的类型有不同的行为。该行为当然是th的行为e组件以及所有子组件。如果需要不同的操作行为,则需要不同的实现。但可能您正在寻找访问者模式?如果操作取决于对象的具体类型而不是其多态行为,则可能不希望在此处使用继承。您可以解决这些问题如前所述,使用基于继承的多态性和访问者。但我认为更干净的方法是根本不使用公共基类,只使用Boost或c++17变体。只有在您始终使用所有可能的“实例”时才有效。如果这是库的一部分,用户可以添加自己的de
    int main() {
        Type t1 = Type1{}; 
        auto a1 = Action{t1};
        a1.print();
    
        auto a2 = Action{Type2{}};  
        a2.print();
    }
    
    auto action = make_combined(
        [](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
        [](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); },
        [](auto const& t) { std::cout << t.print(); } // this will be selected for all other types 
    );
    
    #include <iostream>
    #include <memory>
    
    class Type
    {
        public:
            virtual void Do()=0;
    };
    
    template <typename Base>
    class Type1: public Base
    {
        void Do() override { std::cout << "Type1::Do" << std::endl; }
    };
    
    template <typename Base>
    class Type2: public Base
    {
        void Do() override { std::cout << "Type2::Do" << std::endl; }
    };
    
    template <typename Base>
    class Action: public Base
    {
        public:
        virtual void Print()=0;
    };
    
    template <typename Base>
    class Action1: public Base
    {
        void Print() override { std::cout << "Print for Action1" << std::endl;}
    };
    
    template <typename Base>
    class Action2: public Base
    {
        void Print() override { std::cout << "Print for Action2" << std::endl;}
    };
    
    int main()
    {
    
        // what we want "mixin" is Action into Type
        using Base = Action<Type>;
    
        std::unique_ptr<Base> a1 { new Action1<Type1<Base>> };    // Action1 in Type1
        std::unique_ptr<Base> a2 { new Action2<Type2<Base>> };    // Action2 in Type2
    
        a1->Do();
        a1->Print();
    
        a2->Do();
        a2->Print();
    }
    
    #include <iostream>
    using namespace std;
    
    /** Class Type and its derived classes Type1 and Type2
     */
    class Type {
    public:
        int type;
        virtual void print() = 0;
    };
    class Type1 : public Type {
    public:
        Type1() { type = 1; }
        void print() { cout << "I am of type 1" << endl; }
    };
    class Type2 : public Type {
    public:
        Type2() { type = 2; }
        void print() { cout << "I am of type 2" << endl; }
    };
    
    /** Class Action and its derived classes Action1 and Action2
     */
    class Action {
    protected:
        Type *t;
    public:
        Action(Type *t) : t(t) {};
        static Action *make_action(Type *t);
        virtual void print() = 0;
    };
    class Action1 : public Action {
    public:
        Action1(Type *t) : Action(t) {};
        void print() {
            cout << "I am an action1. My type says: ";
            t->print();
        }
    };
    class Action2 : public Action {
    public:
        Action2(Type *t) : Action(t) {};
        void print() {
            cout << "I am an action2. My type says: ";
            t->print();
        }
    };
    
    Action *Action::make_action(Type *t) {
        switch(t->type) {
        case 1:
            return new Action1(t);
            break;
        case 2:
            return new Action2(t);
            break;
        }
    }
    
    /** Main
     */
    int main(int argc, const char * argv[]) {
        Type *ta = new Type1();
        Action *acta = Action::make_action(ta);
        acta->print();
        delete acta, ta;
    
        Type *tb = new Type2();
        Action *actb = Action::make_action(tb);
        actb->print();
        delete actb, tb;
    
        return 0;
    }
    
    I am an action1. My type says: I am of type 1
    I am an action2. My type says: I am of type 2
    
    #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;
    }
    
    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;
    }