[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Programming ➜ STL ➜ Examples - using lists

Examples - using lists

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Wed 02 Jul 2003 07:09 AM (UTC)

Amended on Wed 24 Sep 2003 02:28 AM (UTC) by Nick Gammon

Message
The example below shows creating a list of people, adding to the tail and the head. We then use for_each to print each one using the Print member function. Then all pointers are deleted.





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

using namespace std;

struct DeleteObject
  {
  template <typename T>
  void operator() (const T* ptr) const { delete ptr; };
  };

class Person
  {
  // private values
  string m_sName;
  string m_sEmail;
  int    m_iAge;

  public:

  // constructor
  Person (const string sName, 
          const string sEmail, 
          const int iAge) :
  m_sName (sName), m_sEmail (sEmail), m_iAge (iAge) {};

  // this is a bit wacky but the MS compiler would not compile
  // (the for_each) without a return value, however who cares 
  // what it is?

  // print this person
  int Print (void) 
    { 
    cout << m_sName 
         << " - "     << m_sEmail 
         << ", age "  << m_iAge 
         << endl;
    return 0;   // return value gets discarded
    };

  };

// a list of people pointers
typedef list<Person*> PersonList;

int main (void)
  {
  PersonList people;

  // add items to list
  people.push_back  (new Person ("Nick", "nick@some-email-address.com", 15));
  people.push_back  (new Person ("Fred", "fred@nurk.com.au", 100));
  people.push_front (new Person ("John", "john@smith.com.au", 35));

  // print everyone (calls member function Print in the person class)
  for_each (people.begin (), people.end (), mem_fun (&Person::Print));

  // delete all objects in list
  for_each (people.begin (), people.end (), DeleteObject ());
  // now delete them from the list
  people.clear ();

  return 0;
  } // end of main


Output

John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100



- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #1 on Thu 03 Jul 2003 01:41 AM (UTC)

Amended on Fri 09 Jul 2004 01:04 AM (UTC) by Nick Gammon

Message
The next example demonstrates various ways of getting information from the data in the list.

To save worrying about pointers and deleting them I have reworked the earlier class to be "straight" (not a list of pointers to people, just a list of people). In order to support this I added a copy and assignment operator.

The various ways of outputting the person's name, email and age are:


  • Via a function built into the class (Person::Print)
  • Via a separate function (PrintPerson)
  • Via a function object (fPrint) which takes an output stream as an argument. Two examples here show outputting directly to cout, or to an output stream (so the output can be a string inside the program).
  • Via an ostream iterator (ostream& operator<<) which lets you stream output from the Person object to an output stream.
  • Using remove_copy_if to selectively output to the ostream iterator - this lets you print a subset of the list.
  • Doing the same thing but to a custom-written iterator - this would let you totally control what was output and where it went.


All of the above may make this example look a bit cluttered, but you only need to use one of the methods in practice, however the example shows there are lots of ways of achieving a similar result.




#include <string>
#include <list>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <iterator>
#include <functional>

using namespace std;

class Person
  {
  // private members
  string m_sName;
  string m_sEmail;
  int    m_iAge;

  public:

  // constructor
  Person (const string sName, 
          const string sEmail, 
          const int iAge) :
   m_sName (sName), m_sEmail (sEmail), m_iAge (iAge) {};

  // default constructor
  Person () : m_iAge (0) {};

  // copy constructor
  Person (const Person & p) :
    m_sName (p.m_sName), m_sEmail (p.m_sEmail), m_iAge (p.m_iAge) {};
    
  // operator =
  Person & operator= (const Person & rhs)
    {
    // don't assign to self
    if (this == &rhs)
      return *this;

    m_sName = rhs.m_sName;
    m_sEmail = rhs.m_sEmail;
    m_iAge = rhs.m_iAge;
    return *this;
    };

  // this is a bit wacky but the MS compiler would not compile
  // (the for_each) without a return value, however who cares 
  // what it is?

  // print this person
  int Print (void) 
    { 
    cout << m_sName 
         << " - "     << m_sEmail 
         << ", age "  << m_iAge 
         << endl;
    return 0;   // return value gets discarded
    };

  // access private members

  string GetName ()   const { return m_sName; };
  string GetEmail ()  const { return m_sEmail; };
  int GetAge ()       const { return m_iAge; };

  }; // end of class Person

// ostream iterator for Person class
ostream& operator<< (ostream& os, const Person & p)
 {
 os << p.GetName () 
     << " - "     << p.GetEmail () 
     << ", age "  << p.GetAge () 
     << endl;
 return os;
 };  // end of ostream& operator<<

// my own iterator can be used for outputting
class myiterator : public iterator <output_iterator_tag, Person>
  {
  public:
    // assignment is used to output the value
    myiterator & operator= (const Person & p)
      { 
      cout << p.GetName () << endl;
      return *this;
      };

    // dereference and increments are no-ops that return 
    // the iterator itself
    myiterator & operator* () { return *this; };
    myiterator & operator++ () { return *this; };
    myiterator & operator++ (int) { return *this; };

  };  // end of class myiterator

// function to print one person
void PrintPerson (const Person & p)
  {
   cout << p.GetName () 
       << " - "     << p.GetEmail () 
       << ", age "  << p.GetAge () 
       << endl;

  } // end of PrintPerson

// function object to print one person
class fPrint 
  {

  ostream & m_os;

  public:

  // constructor - remember which stream to use
  fPrint (ostream & os) : m_os (os) {};

  void operator() (const Person & p)
    {
     m_os << p.GetName () 
         << " - "     << p.GetEmail () 
         << ", age "  << p.GetAge () 
         << endl;
    };

  };  // end of class fPrint

// this function object is used to test a field inside the
// Person class
struct age_less_than : binary_function <Person, int, bool>
  {
  bool operator() (const Person & p, const int iAge) const
    { return p.GetAge () < iAge; };
  };  // end of struct age_less_than

int main (void)
  {
  // make a list of people
  list<Person> people;

  // add items to list
  people.push_back  (Person ("Nick", "nick@some-email-address.com", 15));
  people.push_back  (Person ("Fred", "fred@nurk.com.au", 100));
  people.push_front (Person ("John", "john@smith.com.au", 35));

  // print everyone (calls member function Print in the person class)
  cout << "Using mem_fun_ref ..." << endl;
  for_each (people.begin (), people.end (), mem_fun_ref (&Person::Print));

  // print everyone (calls a separate function to print)
  cout << "Using PrintPerson ..." << endl;
  for_each (people.begin (), people.end (), PrintPerson);

  // print everyone (calls a function object to print)
  cout << "Using fPrint to cout ..." << endl;
  for_each (people.begin (), people.end (), fPrint (cout));

  // for copying results into
  ostringstream os;

  // print everyone to memory
  cout << "Using fPrint to memory ..." << endl;
  for_each (people.begin (), people.end (), fPrint (os));

  cout << "Memory stream contained:" << endl << os.str ();

  // print everyone using an ostream_iterator
  cout << "Using ostream_iterator ..." << endl;
  copy (people.begin (), people.end (), ostream_iterator<Person> (cout));

  // remove_copy_if copies everyone EXCEPT those who meet the condition
  // (thus removing them, in a sense) so we use not1 to reverse that condition

  // use remove_copy_if to show people who meet a condition
  cout << "Show people < 20 years old ..." << endl;
  remove_copy_if (people.begin(), people.end(), 
                  ostream_iterator<Person>(cout), 
                  not1 (bind2nd (age_less_than (), 20)));

  // by using remove_copy_if on its own this time we actually 
  // get people who are NOT less than 50

  // use remove_copy_if to send people who meet a condition to my iterator
  cout << "Show people >= 50 years old ..." << endl;
  remove_copy_if (people.begin(), people.end(), 
                  myiterator (),  // output iterator takes result
                  bind2nd (age_less_than (), 50));

  return 0;
  } // end of main



Output


Using mem_fun_ref ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Using PrintPerson ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Using fPrint to cout ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Using fPrint to memory ...
Memory stream contained:
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Using ostream_iterator ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Show people < 20 years old ...
Nick - nick@some-email-address.com, age 15
Show people >= 50 years old ...
Fred


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #2 on Fri 09 Jul 2004 01:19 AM (UTC)

Amended on Wed 20 Oct 2010 08:10 PM (UTC) by Nick Gammon

Message
Finally I want to illustrate using composing function objects to do the same general thing (find people in an age range) without having to write a special function object to do the compare.

I had trouble doing this in the past, which I now think is due to a) inexperience; and b) the fact the Visual C++ 6 does not correctly compile some templated functions.

The code below compiles under gcc (GCC) 3.2.2, and may well compile under Visual C++ 7, however I haven't tested that. Under Visual C++ 6 I get an error C2664 when compiling it.

What this example does is combine various things, the important lines are in bold.

The functions are:


  • not1 - to reverse the sense of the copy, as remove_copy_if copies everything except the condition specified, so by using not1 we copy everything matching the condition

  • compose_f_gx - this combines two functions (see the Josuttis book for details), effectively giving the results of:


    f(g(element))


    To quote briefly from the book: "the result of calling predicate g () for element is used as input for predicate f ()".

    What this is going to do is first get the person's age (for each element), and then apply the "less" function to each one, with the hard-coded number 40 as what we are comparing it to.

    This is in the "boost" namespace, hence the "using namespace boost" near the start of the file.

  • bind2nd - this is combining "less" and "40" to apply the test "less than 40" to each element

  • less<int> - this is generating the function "less than" for an integer

  • mem_fun_ref - this generates a function call for the argument "by reference", namely "GetAge" for a "Person".



Complicated, huh? No wonder it took a while to get right.

If you want the "compose.hpp" file is is available from the Josuttis site, but to save you the trouble of finding it a copy is available here:

http://gammon.com.au/files/utils/compose.hpp




Finally, the example program uses "transform" to copy the ages only from the first list into another one. Again this uses mem_fun_ref to extract the ages only.



#include <string>
#include <list>
#include <iostream>
#include <iterator>
#include <functional>

// extra include for compose_f_gx function
#include "compose.hpp"

using namespace std;
using namespace boost;

class Person
  {
  // by making the operator<< a friend we can print directly
  // from the member variables - since it is passed a const
  // copy of the Person, it cannot change them
  friend ostream& operator<< (ostream& os, const Person & p);

  // private members
  string m_sName;
  string m_sEmail;
  int    m_iAge;

  public:

  // constructor
  Person (const string sName,
          const string sEmail,
          const int iAge) :
   m_sName (sName), m_sEmail (sEmail), m_iAge (iAge) {};

  // default constructor
  Person () : m_iAge (0) {};

  // copy constructor
  Person (const Person & p) :
    m_sName (p.m_sName), m_sEmail (p.m_sEmail), m_iAge (p.m_iAge) {};

  // operator =
  Person & operator= (const Person & rhs)
    {
    // don't assign to self
    if (this == &rhs)
      return *this;

    m_sName = rhs.m_sName;
    m_sEmail = rhs.m_sEmail;
    m_iAge = rhs.m_iAge;
    return *this;
    };

  // access private members

  string GetName ()   const { return m_sName; };
  string GetEmail ()  const { return m_sEmail; };
  int GetAge ()       const { return m_iAge; };

  }; // end of class Person

// ostream operator<< for Person class
ostream& operator<< (ostream& os, const Person & p)
 {
 os << p.m_sName << " - "     << p.m_sEmail
    << ", age " << p.m_iAge
    << endl;
 return os;
 };  // end of ostream& operator<<


int main (void)
  {
  // make a list of people
  list<Person> people;

  // add items to list
  people.push_back  (Person ("Nick", "nick@some-email-address.com", 15));
  people.push_back  (Person ("Fred", "fred@nurk.com.au", 100));
  people.push_front (Person ("John", "john@smith.com.au", 35));

  // print everyone using an ostream_iterator
  cout << "Using ostream_iterator ..." << endl;
  copy (people.begin (), people.end (), ostream_iterator<Person> (cout));

  // remove_copy_if copies everyone EXCEPT those who meet the condition
  // (thus removing them, in a sense) so we use not1 to reverse that condition

  // use remove_copy_if to show people who meet a condition
  cout << "Show people < 40 years old ..." << endl;
  remove_copy_if (people.begin(), people.end(),
                  ostream_iterator<Person>(cout),
                  not1 (compose_f_gx (
                          bind2nd (less<int> (), 40),
                          mem_fun_ref (&Person::GetAge))
                        ));

  // extract ages using member function

  list<int> ages;
  transform (people.begin(), people.end(), back_inserter (ages),
             mem_fun_ref (&Person::GetAge));

  copy (ages.begin (), ages.end (), ostream_iterator<int> (cout, " "));

  return 0;
  } // end of main



Output


Using ostream_iterator ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
Fred - fred@nurk.com.au, age 100
Show people < 40 years old ...
John - john@smith.com.au, age 35
Nick - nick@some-email-address.com, age 15
35 15 100


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


15,833 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]