tags:

views:

175

answers:

6

In class A, there is a vector V. V is a private member.

In class B, I want to print all items of V. What is the best way to do this?

It is very easy to get an iterator of a vector in the same class, but not easy in another class.

Thank you for reading.

+3  A: 

I A owns the vector, it's up to A to print it. Expose as little as possible, especially for private members.

An alternative would be the visitor pattern; this keeps the data encapsulated, yet allows a lot of flexibility.

struct A {
   template< typename F > void each( F& f ) const {
     std::for_each( data.begin(), data.end(), f );
   }

private:
   std::vector<int> data;
};

struct B : public A {
  static void tprint()( int i )  { std::cout<<i<<std::endl; }
  void print()const {
    each( tprint );
  }
};
xtofl
That’s not really an example of the visitor pattern (visitors implement double-dispatch) but it’s nice code nonetheless.
Konrad Rudolph
It's not directly the O-O version, indeed. It reflects what's called an 'internal' visitor. You can both provide a function or an object as visitor. The 'visit' function is represented by the `operator()`
xtofl
I don't agree with _"If `A` owns the vector, it's up to `A` to print it"_. If this was so, `std::vector` (owning its data) would have to have a print function. Your alternative solution I like very much. Note, however, that currently (IIRC, this changes in C++1x) it is not allowed to use function-local types as template arguments.
sbi
@sbi: you're right, I was overgeneralizing, I meant A doesn't need to expose it's private data - sort of the Law of Demeter. I'll move the local type out, thanks for the note.
xtofl
@xtofl: Now that `tprint` isn't a function object anymore, the compiler will probably be unable to inline calls to it. Since in this concrete example it's writing to the console, this shouldn't make any noticable difference. But in general using function objects has a higher chance of performing better.
sbi
+2  A: 

If B wants to print A’s vector, that’s a request to A, so A should have a method in its public interface to make that possible, e.g. (say V has type vector<int>):

class A {
…
public:
    void dump_values(std::ostream& out) const {
        std::copy(V.begin(), V.end(), std::ostream_iterator<int>(out, " "));
    }
}
Konrad Rudolph
If this was true, `A` would have to have a method for every operation imaginable to be performed with the vector's data. I strongly disagree.
sbi
@sbi: you don’t know that from the question, and your comment is honestly bogus because the alternative would be to expose the vector. Exposing the vector in *any* form only makes sense if more than one operation would be performed on it (as you mentioned). My code, on the other hand, hides unnecessary detail while exposing exactly the interface requested. I agree that it shouldn’t get too specific lest it prevent reuse. But given the scarce information from the question, *we* can’t decide this here. My code is a completely valid use case of a facade.
Konrad Rudolph
@Konrad: Both Wilka and xtofl have found solutions that don't expose the vector (I don't count the typedef'd iterator as this only requires a recompilation and can be circumvented by using a pimpl'd iterator) and yet don't implement specific operations in `A`. I find both solutions better than yours. That might just be me, but that's what I am. `:)`
sbi
I can’t follow your blanket judgement. Personally, I would also prefer xrofl’s method *if* such degree of genericity is necessary (I upvoted it, and commented accordingly) – but the simplest solution is always the best. If such a generic solution isn’t going to be used, don’t implement it. That’s a judgement call, but it’s still an objective decision, not a subjective one.
Konrad Rudolph
@Konrad: <shrug> It might be me having written too much library code in the last decade. But then I also don't see how adding a special-purpose function to a class is simpler than it accepting a functor...
sbi
A: 

Without knowing much about the specifics of the responsibilities of class A and B, I would suggest class A return a const reference to the vector stored in class B. This gives you read-only access to the vector and does not require the performance hit of copying.

  class B
  { 
  private:
      std::vector<int> m_vector;
  public:
      const std::vector<int>& GetVector() {return m_vector}
  }


  class A
  {
  private:
       B m_b;
  public:
     void printVector()
     {
         const std::vector<int>& refToVec = m_b.GetVector();
         std::vector<int>::const_iterator iter = refToVec.begin();
         for (; iter != refToVec.end(); ++iter)
         {
            std::cout << *iter << std::endl;
         }
     }
  }
Doug T.
Returning a direct reference to an internal piece of data is probably the worst idea I could imagine. Now everybody depends on `B` having an object of type `std::vector<int>` that's exposed in the interface. If you measure and find that `std::list<int>` would be faster, you'll have to change all code depending on `A`. -1
sbi
probably, but it does directly answer the question.
Doug T.
+1  A: 

If V is a private member then you have to ask why you would want to get to it from outside of the class? To avoid violating the encapsulation of this data you have several options:

  1. Print all items of V from within A.
  2. Pass an instance of B to A so that it can use it to print the items from V.

E.g. :

A::printInternalData( const B& printer ) { printer.print( V ); };

Besides this you must expose V to the outside world.

J.Churchill
A: 

What about implementing this_stuff_begin() and this_stuff_end() members that return iterators for the vector? It's not clear what's the use of having it private though…

Michael Krelin - hacker
+5  A: 

Taking your point:

It is very easy to get an iterator of a vector in the same class, but not easy in another class.

(and assuming that wanting to print the elements is a just a place holder for some other complicated action)

You can still expose the an iterator for your vector from A that can be used inside of B via a typedef, e.g.

class A
{
private:
    std::vector<int> V;
public:
    typedef std::vector<int>::const_iterator const_iterator;
    const const_iterator begin() const
    {
     return V.begin();
    }

    const const_iterator end() const
    {
     return V.end();
    }
};

Then you can use those iterators like this:

class B
{
public:
    void Foo()
    {
     A a;
     // do stuff that will put things into the collection inside 'a'
     std::copy(a.begin(), a.end(), std::ostream_iterator<int>(std::cout, " "));
    }
};

I've used const_iterator in this case, because you asked for read-only access, but you can use iterator instead if you need write access (although that's likely to be a poor design choice, writing to an internal member like that).

Wilka
That top-level `const` on returning values is nonsense, but doesn't any harm either. I consider this a very good answer. +1
sbi
Thank you, Wilka.
jonguk