How to print container elements which are pointers

Discussion in 'C++' started by V.Subramanian, India, Nov 9, 2012.

  1. In the following program,
    I have kept the data members ONLY for learning purpose.

    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <list>
    #include <algorithm>

    using namespace std;

    class Person {

    public:
    explicit Person(const string& arg);
    string name;
    };

    inline Person::person(const string& arg) : name(arg)
    {
    }

    class Club {

    public:
    list<Person*> officers;
    };

    class Print_name {

    public:
    Print_name(ostream& os);
    void operator()(const Person* const & arg) const;

    private:
    ostream& out;
    };

    inline Print_name::print_name(ostream& os) : out(os)
    {
    }

    inline void Print_name::eek:perator()(
    const Person* const & arg) const
    {
    out << arg->name << endl;
    }

    int main()
    {
    Club c;

    c.officers.push_back(new Person(string("one")));
    c.officers.push_back(new Person(string("two")));
    c.officers.push_back(new Person(string("three")));

    for_each(c.officers.begin(),
    c.officers.end(),
    Print_name(cout));

    return EXIT_SUCCESS;
    }

    The above program uses 'std::for_each()'
    algorithm. The third argument to this
    algorithm is a function object 'Print_name'.
    The argument to the function object is the
    output stream into which we want to write
    the contents.

    Since the container elements are pointers,
    I am unable to use the 'std::copy()' algorithm
    with ostream_iterator because the value
    which will be passed to ostream_iterator
    is a pointer.

    something like:
    copy(c.officers.begin(),
    c.officers.end(),
    ostream_iterator<Person*>(cout, "\n"));
    This wil simply print the memory addresses
    to the standard output. Am I correct ?

    Is there a clean solution instead of the
    'std::for_each()' algorithm with the
    'Print_name' function object
    combination which I have used in the
    above program ?

    Please provide the solution.

    Is it possible to use the 'std::copy()' algorithm
    for the above scenario ?

    Thanks
    V.Subramanian
    India.
    V.Subramanian, India, Nov 9, 2012
    #1
    1. Advertising

  2. V.Subramanian, India

    SG Guest

    On Nov 9, 3:36 am, "V.Subramanian, India" wrote:
    > [...]
    > Since the container elements are pointers,
    > I am unable to use the 'std::copy()' algorithm
    > with ostream_iterator because the value
    > which will be passed to ostream_iterator
    > is a pointer.
    >
    > something like:
    > copy(c.officers.begin(),
    >         c.officers.end(),
    >         ostream_iterator<Person*>(cout, "\n"));
    > This will simply print the memory addresses
    > to the standard output. Am I correct ?


    Yes. Unless you provide an overload that treats Person pointers
    differently, like Luca suggested.

    > Is there a clean solution instead of the
    > 'std::for_each()' algorithm with the
    > 'Print_name' function object
    > combination which I have used in the
    > above program ?
    > [...]
    > Is it possible to use the 'std::copy()' algorithm
    > for the above scenario ?


    Sure. There are lots of "clean" solutions. The easiest one is probably
    the one using for-range:

    for (auto& pp : c.officers) {
    cout << pp->name << endl;
    }

    If you want to use std::copy with an ostream_iterator, you could use
    boost::indirect_iterator

    :::
    ostream& operator<<(ostream& os, Person const& x);
    :::

    copy(boost::make_indirect_iterator(c.officers.begin()),
    boost::make_indirect_iterator(c.officers.end()),
    ostream_iterator<Person>(cout));

    if you don't like to overload operator<< for Person pointers.
    SG, Nov 9, 2012
    #2
    1. Advertising

  3. On 2012-11-08 18:36, V.Subramanian, India wrote:
    >
    > Since the container elements are pointers,
    > I am unable to use the 'std::copy()' algorithm
    > with ostream_iterator because the value
    > which will be passed to ostream_iterator
    > is a pointer.
    >
    > something like:
    > copy(c.officers.begin(),
    > c.officers.end(),
    > ostream_iterator<Person*>(cout, "\n"));
    > This wil simply print the memory addresses
    > to the standard output. Am I correct ?
    >
    > Is there a clean solution instead of the
    > 'std::for_each()' algorithm with the
    > 'Print_name' function object
    > combination which I have used in the
    > above program ?


    If for some reason you don't want std::for_each and you don't
    want an overloaded operator<<(ostream& o, const Person* p),
    you can use std::transform to "transform" the pointers to objects
    of a type designated for printing the names:

    class Print_name {
    const Person* person;
    public:
    Print_name(const Person* p) : person(p) { }
    static Print_name create(const Person* p) { return Print_name(p); }
    friend ostream& operator<<(std::eek:stream& os, Print_name pn)
    { os << pn.person->name; }
    };

    std::transform(c.officers.begin(),
    c.officers.end(),
    std::eek:stream_iterator<Print_name>(std::cout, "\n"),
    Print_name::create);

    You may want to avoid making a indirect function call to
    Print_name::create for each Person pointer and inline the call to the
    constructor by employing a class type:

    class Print_name {
    // ...
    struct creator {
    Print_name operator()(const Person* p) const
    { return Print_name(p); }
    };
    // ...
    };

    std::transform(c.officers.begin(),
    c.officers.end(),
    std::eek:stream_iterator<Print_name>(std::cout, "\n"),
    Print_name::creator());

    This way, everything is inline, and the Print_name objects that get
    passed around are only as big as a Person pointer, so the compiled
    code can be very efficient.

    --
    Seungbeom Kim
    Seungbeom Kim, Nov 11, 2012
    #3
  4. V.Subramanian, India

    Zhihao Yuan Guest

    On Thursday, November 8, 2012 8:36:30 PM UTC-6, V.Subramanian, India wrote:
    > c.officers.push_back(new Person(string("one")));


    To expose a pointer-based interface is very dangerous, since it's too
    easy to leak a new-ed memory (your sample program already leaked) or
    encounter a data corruption if you insist to delete the pointers
    somewhere.

    Anyway, here is my suggestion: to define a custom iterator.

    First, as usual, you need an output operator for Person:

    friend ostream& operator<<(ostream& out, Person const& p) {
    out << p.name;
    return out;
    }

    And then, a proxy-like iterator in Club:

    class Club {

    public:
    struct iterator : list<Person>::iterator {
    explicit iterator(list<Person*>::iterator i) : ptr_(i) {}

    iterator(iterator const& i) : ptr_(i.ptr_) {}

    iterator& operator=(iterator i) {
    using std::swap;
    swap(this->ptr_, i.ptr_);
    return *this;
    }

    reference operator*() const {
    return **ptr_;
    }

    pointer operator->() const {
    return &(operator*());
    }

    iterator& operator++() {
    ++ptr_;
    return *this;
    }

    iterator operator++(int) {
    iterator tmp(*this);
    ++(*this);
    return tmp;
    }

    /* operator-- omitted */

    friend bool operator==(iterator x, iterator y) {
    return x.ptr_ == y.ptr_;
    }

    friend bool operator!=(iterator x, iterator y) {
    return !(x == y);
    }

    private:
    list<Person*>::iterator ptr_;
    };
    list<Person*> officers;

    iterator begin() {
    return iterator(officers.begin());
    }

    iterator end() {
    return iterator(officers.end());
    }
    };

    So that you can use

    copy(c.begin(),
    c.end(),
    ostream_iterator<Person>(cout, "\n"));

    To print the Club. Cool?
    Zhihao Yuan, Nov 27, 2012
    #4
  5. V.Subramanian, India

    Zhihao Yuan Guest

    Sorry, one important error.

    On Monday, November 26, 2012 6:41:59 PM UTC-6, Zhihao Yuan wrote:
    > struct iterator : list<Person>::iterator {


    I extended an iterator for the typedefs, but got an base class
    subobject unintentionally. Should be:

    class Club {

    typedef list<Person>::iterator _IterT;

    public:
    struct iterator : std::iterator<
    _IterT::iterator_category,
    _IterT::value_type,
    _IterT::difference_type,
    _IterT::pointer,
    _IterT::reference
    > {


    Or just a list of public typedefs.

    So sorry.
    Zhihao Yuan, Nov 27, 2012
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Wolfgang Lipp
    Replies:
    1
    Views:
    390
    Patrick TJ McPhee
    Jan 30, 2004
  2. Wolfgang Lipp
    Replies:
    0
    Views:
    463
    Wolfgang Lipp
    Jan 28, 2004
  3. Replies:
    5
    Views:
    2,952
    Earl Purple
    Dec 18, 2005
  4. Replies:
    4
    Views:
    784
    Daniel T.
    Feb 16, 2006
  5. Hicham Mouline
    Replies:
    1
    Views:
    382
    Kai-Uwe Bux
    Apr 11, 2010
Loading...

Share This Page