Is it possible to change a C++ object's class after instantiation?

后端 未结 16 2203
忘掉有多难
忘掉有多难 2021-02-02 06:50

I have a bunch of classes which all inherit the same attributes from a common base class. The base class implements some virtual functions that work in general cases, whilst eac

16条回答
  •  不要未来只要你来
    2021-02-02 07:19

    DISCLAIMER: The code here is provided as means to understand an idea, not to be implemented in production.

    You're using inheritance. It can achieve 3 things:

    • Add fields
    • Add methods
    • replace virtual methods

    Out of all those features, you're using only the last one. This means that you're not actually forced to rely on inheritance. You can get the same results by many other means. The simplest is to keep tabs on the "type" by yourself - this will allow you to change it on the fly:

    #include 
    
    enum MyType { BASE, DERIVED };
    
    class Any {
    private:
        enum MyType type;
    public:
        void whoami() { 
            switch(type){
                case BASE:
                    std::cout << "I am Base\n"; 
                    return;
                case DERIVED:
                    std::cout << "I am Derived\n"; 
                    return;
            }
            throw std::runtime_error( "undefined type" );
        }
        void changeType(MyType newType){
            //insert some checks if that kind of transition is legal
            type = newType;
        }
        Any(MyType initialType){
            type = initialType;
        }
    
    };
    

    Without inheritance the "type" is yours to do whatever you want. You can changeType at any time it suits you. With that power also comes responsibility: the compiler will no longer make sure the type is correct or even set at all. You have to ensure it or you'll get hard to debug runtime errors.

    You may wrap it in inheritance just as well, eg. to get a drop-in replacement for existing code:

    class Base : Any {
    public:
        Base() : Any(BASE) {}
    };
    
    class Derived : public Any {
    public:
        Derived() : Any(DERIVED) {}
    };
    

    OR (slightly uglier):

    class Derived : public Base {
    public:
        Derived : Base() {
            changeType(DERIVED)
        }
    };
    

    This solution is easy to implement and easy to understand. But with more options in the switch and more code in each path it gets very messy. So the very first step is to refactor the actual code out of the switch and into self-contained functions. Where better to keep than other than Derivied class?

    class Base  {
    public:
        static whoami(Any* This){
            std::cout << "I am Base\n"; 
        }
    };
    
    class Derived  {
    public:
        static whoami(Any* This){
            std::cout << "I am Derived\n"; 
        }
    };
    
    /*you know where it goes*/
        switch(type){
            case BASE:
                Base:whoami(this);
                return;
            case DERIVED:
                Derived:whoami(this);
                return;
        }
    

    Then you can replace the switch with an external class that implements it via virtual inheritance and TADA! We've reinvented the Strategy Pattern, as others have said in the first place : )

    The bottom line is: whatever you do, you're not inheriting the main class.

提交回复
热议问题