Search code examples
c++inheritancedesign-patternssubclasssuperclass

Creating multiple instances of a subclass, with only one instance of a superclass


I'm trying to implement the Flyweight pattern, but I'm not quite sure how inheritance works, and so I'm not quite sure how this pattern works.

Let's say I have a superclass that holds all the "heavy" information - textures, etc., the instrinsic information (the one that never changes).

class Block{
public: //for the sake of the example
Texture tex; 
etc.
};

And I have the light class with data that changes:

class Block_light : public Block {
public:
int posX, posY, posZ;
int color;
etc.
};

So, if I in main create n Block_light, will I be also creating n Block or will they all be tied to one instance?

 int main(){
 std::vector<Block_light> blocks(n);
 return 0;
 };

Did I create n Block_light and 1 Block or n Block_light and n Block? If it's the latter, how can I make it so that it only uses one instance?


Solution

  • The C++ object model ensures that each instance of a subclass (i.e. derived class) contains also its own instances of all its superclasses (i.e. base classes).

    In your case, this means that every Block_light object will have all the Block_light properties, as well as a Block sub-object with all the Block properties. In other words, Block_light are not so light at all.

    If you want to share a single common state:

    • You could use composition instead of inheritance: the Block_light would not inherit from Block but refer to a shared Block object.
    • If you nevertheless need to be able to use Block and Block_light interchangeably, you could make both inherit from a common interface (i.e. a clss with only virtual functions and no state) IBlock.
    • You could alternatively use the flyweight pattern, in which parts of the state (shared or not) is "externalized" into an extrinsinc state. The particularity of this pattern is that the reference to the extrinsic state is provided to the classes functions by the caller, so that the flyweight object does not need to store a pointer to such a state.

    The first option would look like:

    class Block {...}; 
    class Block_light {       // no inheritance 
        shared_ptr<Block> b;  // but (private) composition   
        ...
    };  
    

    The second option would be:

    class IBlock {  // no member variables
    public:
        virtual Texture get_texture()=0;
        virtual ~IBlock(){}; 
    };  
    
    class Block : public IBlock {  // sharable state
        Texture tex; 
    public: 
        Texture get_texture() override { return tex; }
    };    
    
    class Block_light : public IBlock {
        shared_ptr<IBlock> b;  // or IBlock or Block depending on the needs.  
    public:
        Block_light (shared_ptr<IBlock>i) : b(i) {}
        Texture get_texture() override { return b->get_texture(); }
    };  
    
    int main() { // just a perfectible quick example
        auto myb=make_shared<Block>();
        Block_light b1(myb);
        b1.get_texture();
    }
    

    The last one would be used like this:

    int main() { // just a perfectible quick example
        Block myb;     // shared part; 
        Block_light bl;
        bl.get_texture(myb);
    }
    

    I will not enter into the details of the flyweight implementation but you have an example here. However think twice before opting for this pattern, since providing the shared context can be challenging and error prone.