abstract classes in std containers

前端 未结 5 1676
深忆病人
深忆病人 2020-12-28 19:22

Very often, when I program, I use polymorphism because it naturally models the objects that I need. On the other hand I very often use standard containers to store these obj

5条回答
  •  醉酒成梦
    2020-12-28 19:36

    How about writing a wrapper around foo that incapsulates foo* and implicitly converts to foo&?

    It uses a copy semantic that calls the underlying clone on the stored object to make a deep copy. This is at least not worse than the original intent to store everything by value. If you end up storing everything as a pointer to abstract base anyway then this has the same level of indirection as a unique_ptr but is copyable (whereas unique_ptr is not). On the other hand this is less overhead than shared_ptr.

    Add clone() to the abstract hierarchy:

    struct foo {
        virtual void print() const = 0;
    
        virtual ~foo() {};
        virtual foo* clone() = 0;
    };
    
    struct goo : public foo {
        int a;
        void print() const { std::cout << "goo" << std::endl; }
        foo* clone() { return new goo(*this); }
    };
    
    struct moo : public foo {
        int a,b;
        void print() const { std::cout << "moo" << std::endl; }
        foo* clone() { return new moo(*this); }
    };
    

    Define foo_w wrapper around foo, see copy-and-swap idiom.

    struct foo_w {
        foo_w(foo *f = nullptr) : fp(f) {}
        ~foo_w() { delete fp; }
    
        foo_w(const foo_w& that) : fp(that.fp->clone()) {}
        foo_w(foo_w&& that) : foo_w() { swap(*this, that); }
    
        foo_w& operator=(foo_w rhs) {
           swap(*this, rhs);
           return *this;
        }
    
        friend void swap(foo_w& f, foo_w& s) {
           using std::swap;
           swap(f.fp, s.fp);
        }
    
        operator foo&() { return *fp; } 
        operator const foo&() const { return *fp; } 
    
        foo& get() { return *fp; }
        const foo& get() const { return *fp; }
    
        // if we rewrite interface functions here
        // calls to get() could be eliminated (see below)
        // void print() { fp->print(); };
    
    private:
        foo *fp;
    };
    

    The usage is as follows:

    #include 
    #include 
    #include 
    
    // class definitions here...
    
    int main() {
        std::vector foos;
        foos.emplace_back(new moo);
        foos.emplace_back(new goo);
        foos.emplace_back(new goo);
        foos.emplace_back(new moo);
    
        // variant 1: do it through a getter:
        for(auto it = foos.begin(); it!=foos.end(); ++it) {
            it->get().print();
            // the presence of a proxy is almost hidden
            // if we redefine interface in foo_w
            // it->print();
        }
    
        // variant 2: use it through reference to foo
        for(auto it = foos.begin(); it!=foos.end(); ++it) {
            foo& fr = *it;
            fr.print();
        }
    
        // variant 3: looks really nice with range-for
        for(foo& fr : foos)
            fr.print();
    
        return 0;
    }
    

    The wrapper behavior is really up to what suits your needs. Probably if you're OK with unique_ptr being not copyable that one is a better way to go, for me it was critical so I ended up with this. Also have a look at std::reference_wrapper to store reference-like objects in the container.

提交回复
热议问题