开发者

Vector, Size_type, and Encapsulation

开发者 https://www.devze.com 2022-12-19 18:26 出处:网络
I have a class wi开发者_开发知识库th a private data member of type vector< A*>. The class has two public methods that actually use vector<A*>::size_type:

I have a class wi开发者_开发知识库th a private data member of type vector< A*>.

The class has two public methods that actually use vector<A*>::size_type:

  1. Method returning number of elements in the vector
  2. Method returning element in the vector by index

I can add to public section of the class the following typedef:

typedef vector::size_type SIZE_t;

but IMHO it exposes too many details about class implementation.

Another approach is to use size_t.

What do you think?


I would use a typedef in the class. The reason is that for std::vector, the size type is std::size_t, but if you later change the code to use a container (hand rolled) whose size type is not std::size_t redefining the typedef will be enough.

Using that typedef does not expose any detail of implementation, and it in fact helps encapsulate. The important element in the typedef is the local name, not what it is defined to be.

for ( mytype::size_type i = 0; i < myelement.size(); ++i )

In the for loop above, user code is unaware of whether size_type is a signed or unsigned type, it just works. You can change your implementation, and as long as you update the typedef the previous code will compile without signed/unsigned comparison warnings. The typedef actually helps encapsulation.


Use plain old size_t for both member functions.


Which details would those be? The only thing size_type exposes is the size needed for indexing (which will almost certainly be size_t). Adding the typedef doesn't expose any more information.


All size_t types are fundamentally the same scalar type and since its scalar, it is implicitly convertible for the compiler. So there is no compile-time or runtime difference between using std::size_t, std::vector::size_type or any other similar type.

It is a good idea (and adhering to conventions) to provide a typedef for the size type in your class. IMO the typedef you show does not expose too much of your implementation, since clients are supposed to use your typedef, not vector::size_type directly. But if you prefer

typedef std::size_t SIZE_T;

that looks equally fine to me.


If you want to have the greatest level of encapsulation, then I would use:

private:
    typedef std::vector<A*> container_type;
    container_type _container;
public:
    typedef container_type::const_iterator const_iterator;

    const_iterator begin()const{ return _container.begin(); }
    const_iterator end()const{ return _container.end(); }

By using iterators instead of the size type, you would be able to switch between std::vector and std::list. However, if random access is a requirement for your class, then I would go with:

private:
    typedef std::vector<A*> container_type;
    container_type _container;
public:
    typedef container_type::size_type size_type;
    A* operator[](size_type idx)const{ return _container[idx]; }
    size_type size()const{ return _container.size(); }

If the user of your class does not need to be able to iterate through the contents of the internal container, then I would simply keep the typedefs private and not provide those public accessor functions.


If your class is already using std::vector<A*> in its implementation, then adding typedef std::vector<A*>::size_type size_type does not expose any more details than it already exposes.

However, if you are going for full encapsulation, you would want to a technique such as the PIMPL idom or an interface class (also known as protocol class), completely hiding that std::vector<A*> is used in the implementation at all:

Before:

#include <vector>
class A;
class Foo {
public:
    typedef std::vector<A*>::size_type size_type;
    size_type get_number_of_stuff() const;
private:
    std::vector<A*> _stuff;
};

After (using PIMPL technique):

class FooImpl;
class Foo {
public:
    typedef size_t size_type; // or just use size_t directly below
    size_type get_number_of_stuff() const;
private:
    FooImpl* _impl;
};

FooImpl is defined in your source file, not the header, completely hiding the choice of vector in the implementation details. Therefore, you no longer need to #include <vector> in your header file anymore, which has a couple benefits:

  • Users of your header file don't have to (indirectly) include vector if they don't use it. This can improve compile-time performance, which is important in larger code bases.
  • If you change the implementation (e.g., to list), you don't risk breaking any code that (erroneously) relied on your #include <vector>, which is now #include <list>. It happens.
0

精彩评论

暂无评论...
验证码 换一张
取 消