开发者

C++ composition with iterator

开发者 https://www.devze.com 2023-01-14 15:49 出处:网络
I\'ll try and keep my sample code very straightforward, but it may have errors as I\'m typing it on the spot.

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 cl开发者_开发知识库ient 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;
}

P.S. 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?

0

精彩评论

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