What is the C++ equivalent of inheriting a Java collection interface (Set, Map, List etc.)? Or extending AbstractCollection?

后端 未结 5 1379
死守一世寂寞
死守一世寂寞 2020-12-21 02:08

I\'ve started coding in C++, coming from a Java background (actually I\'d studied C++ at my university, but we never got to the STL etc.)

Anyway, I\'ve gotten to the

5条回答
  •  难免孤独
    2020-12-21 02:49

    You need to try and let go of the Java mindset. You see, the beauty of STL, is that it separates algorithms from containers through iterators.

    Long story short: Pass around iterators to your algorithms. Don't inherit.

    Here are all the containers: http://en.cppreference.com/w/cpp/container

    And here are all the algorithms: http://en.cppreference.com/w/cpp/algorithm

    There may be two reasons why you may want to inherit:

    • You want to reuse implementation (bad idea)
    • Reuse existing algorithms by making a behavior available (e.g. inheriting from a base class like AbstractSet)

    To briefly touch upon the first point, if you need to store an array of things (say an array of objects in a game scene), do exactly that, have an array of these objects as a member to the Scene object. There is no need to subclass to fully utilize the container. In other words, prefer composition over inheritance. This has been done to death already, and is accepted in the Java world as doing "The Right Thing". See discussion here, it's in the GoF book! Same thing applies to C++.

    Example:

    To address the second point let's consider a scenario. You are making a 2D sidescroller game, and you have a Scene object, with an array of GameObjects. These GameObjects have positions, and you'd like to sort them by position, and do binary search to find the closest object, as an example.

    In the C++ mindset, the storage of elements and manipulation of containers are two separate things. The container classes provide the bare minimum functionality, for creation/insertion/removal. Anything interesting above that is relegated to Algorithms. And the bridge between them are iterators. The idea is that whether you use std::vector (equivalent to Java's ArrayList I think), or your own implementation is irrelevant as long as access to elements is the same. Here is a contrived example:

    struct GameObject {
        float x, y;
    
        // compare just by x position
        operator < (GameObject const& other)
        {
            return x < other.x;
        }
    };
    
    void example() {
        std::vector objects = {
            GameObject{8, 2},
            GameObject{4, 3},
            GameObject{6, 1}
        };
        std::sort(std::begin(objects), std::end(objects));
        auto nearestObject = std::lower_bound(std::begin(objects), std::end(objects), GameObject{5, 12});
    
        // nearestObject should be pointing to GameObject{4,3};
    }
    

    Things to note here, the fact that I used std::vector to store my objects, doesn't matter as much as the fact I can perform random access on its elements. The iterators returned by the vector capture that. As a result we can sort and perform binary search.

    The essence of the vector is random access to elements

    We can swap out the vector for any other random access structure, without inheritance, and the code still works perfectly fine:

    void example() {
        // using a raw array this time.
        GameObject objects[] = {
            GameObject{8, 2},
            GameObject{4, 3},
            GameObject{6, 1}
        };
        std::sort(std::begin(objects), std::end(objects));
        auto nearestObject = std::lower_bound(std::begin(objects), std::end(objects), GameObject{5, 12});
    
        // nearestObject should be pointing to GameObject{4,3};
    }
    

    For reference, see the functions I have used:

    • std::sort
    • std::lower_bound

    Why is this a valid alternative to inheritance?

    This approach gives two orthogonal directions for extensibility:

    • New containers can be added, without inheritance, just by providing iterator access. All existing algorithms work.
    • New algorithms can be added. All containers supporting these iterators will work with these new algorithms, past, present or future.

提交回复
热议问题