How to print container elements which are pointers

  • Thread starter V.Subramanian, India
  • Start date
V

V.Subramanian, India

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

SG

[...]
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.
 
S

Seungbeom Kim

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.
 
Z

Zhihao Yuan

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?
 
Z

Zhihao Yuan

Sorry, one important error.

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top