简体   繁体   中英

C++ composition with iterator

I'll try and keep my sample code very straightforward, but it may have errors as I'm typing it on the spot.

I have a class named Phone.

class Phone
{
 public:
  Phone(std::string manufacturer, std::string model, std::vector<Feature> features);

 private:
  std::vector<Features> features;
  std::string model;
  std::string manufacturer;
};

I have a struct named Feature.

struct Feature
{
   Feature(std::string feature, std::string category);
   std::string feature;
   std::string category;
};

As you can see a phone has a list (vector) of features: ie. bluetooth, GPS, radio, etc, which have a category: networking, navigation, multimedia.

Now information about phones and features are stored in a sqlite3 database. I have a helper function which will retrieve a particular phone model from the database and return a populated Phone object. I also have a function which takes in a Phone object and writes the Phone to the database.

The problem is I need to provide the client some way of iterating over the list of Features of a Phone. For starters the database helper needs to know about the features so it can write them to the database. Second the client may need to retrieve a Phone from the database and then display the list of features to the user.

One solution is to add the following functions in class Phone

std::vector<Feature>::iterator begin()
std::vector<Feature>::iterator end()

This is not an ideal solution for me because the client code will not look intuitive - it will appear as if the client is iterating over a Phone, when they are actually iterating over Features.

Another solution is discussed in an article at http://accu.org/index.php/journals/1527 which discusses using a technique named "memberspaces" to expose iterators to client code. It would result in more readable client code, but the implementation is a bit messy in my opinion.

Is there a better solution to the iterator problem, or maybe there is a more suitable design pattern I could be using.

Any feedback would be greatly appreciated.

In your case I would go for better names first:

typedef std::vector<Feature> Features;

Features::iterator features_begin();
Features::iterator features_end();
Features::const_iterator features_begin() const;
Features::const_iterator features_end() const;

Examples:
1)

 // Note: you'll need to define an operator<< for Feature
 // can be used with std::algorithms
 std::copy( phone.features_begin(), phone.features_end(),
   std::ostream_iterator<Feature>( std::cout, "\n\r" ) );     

2)

// Note: shamelessly borrowed from http://www.boost.org/doc/libs/1_44_0/doc/html/foreach/extensibility.html
// add overloads of range_begin() and range_end() for Phone::Features
inline Phone::Features::iterator range_begin( Phone& phone ){
   return phone.features_begin();
}

inline Phone::Features::iterator range_end( Phone& phone ){
   return phone.features_end();
}

namespace boost{
   // specialize range_mutable_iterator and range_const_iterator in namespace boost
   template<>
   struct range_mutable_iterator< Phone >{
      typedef Phone::Features::iterator type;
   };

   template<>
   struct range_const_iterator< Phone >{
      typedef Phone::Features::const_iterator type;
   };
}
...
// can be used with BOOST_FOREACH
BOOST_FOREACH( Feature x, phone ){
   std::cout << x << std::endl;
}

PS Given Jonannes' suggestion and the naming convention used by boost::range the names are now features_xxx() instead of xxx_features() (especially since they do make more sense in this context).

If the client code is going to use BOOST_FOREACH, why not just add the method

const std::vector<Feature>& getFeatures() const;

to Phone?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM