What .NET Developers Must Know about C++ Classes: Listing 6

Initializer lists used in structs and user-defined containers.

#include <iostream>
#include <string>
#include <sstream>
#include <initializer_list>
#include <algorithm>
#include <vector>

using namespace std;

// unless specified otherwise, everything in a struct gets public access:
struct Person {
  string firstName, lastName;
  // concatenates first and last name, with a space in between
  string getFullName() {
    stringstream ss;
    ss << firstName << ' ' << lastName;
    return ss.str();
  }
};

// a common functionality for a list of Person. Not instantiable.
class AbstractPersonList {
protected:
// Pure virtual function that pushes elements to the list
  virtual void push_back(Person p) = 0;
// reusable function to be called by derived constructors. It fills the list with the
// STL initializer_list of Person received as parameter
  inline void initStorage(initializer_list<Person> personList) {
    // this is a C++11 notation known as a range-based for loop.
    // Iterates over the parameter, pushing every Person element to the list
    for (Person p : personList)
      push_back(p);
  }
};

// Person list implementation over an STL vector
class VectorPersonList : public AbstractPersonList {
private:
  vector<Person> storage_;
protected:
  inline void push_back(Person p) { storage_.push_back(p); }
public:
// Initializer-list constructor.
  VectorPersonList(initializer_list<Person> personList) {
    initStorage(personList);
  }

  inline vector<Person>::iterator begin() { return storage_.begin(); }
  inline vector<Person>::iterator end() { return storage_.end(); }
};

// Person list implementation over a primitive array Person[]
class ArrayPersonList : public AbstractPersonList {
private:
  size_t size_;
  Person* storage_;
protected:
  inline void push_back(Person p) {
    static size_t index = 0;
    storage_[index++] = p;
  }
public:
// Initializer-list constructor.
  ArrayPersonList(initializer_list<Person> personList) :
          size_{personList.size()}, storage_{nullptr} {
    if (size_>0) {
      // allocates storage space based on the initializer list size
      storage_ = new Person[size_];
      initStorage(personList);
    }
  }
  // A destructor is needed to release any memory allocated at construction
  ~ArrayPersonList() {
    if (storage_!=nullptr)
      delete[] storage_;
  }

  Person* begin() { return storage_; }
  Person* end() { return storage_ + size_; }
};

int main() {
  // C++03 scope of initializer-lists: structs and classes. Notice that the
  // assignment ('=' sign) is optional.
  Person moe = { "Moe", "Howard" }, larry = { "Larry", "Fine" },
      shemp{ "Shemp", "Howard" }, curly{ "Curly", "Howard" };
  // In C# this is equivalent to:
  // Person moe = new Person { firstName = “Moe”, lastName = “Howard” };

  // C++11 initializer-list enhancement: array-like notation that initializes
  // the list calling the type initializer-list constructor
  VectorPersonList vpl = { moe, larry, shemp };
  ArrayPersonList apl = { moe, curly, larry };
  // In C#, if the type implements IEnumerable, this is equivalent to:
  // ArrayPersonList apl = new ArrayPersonList { moe, curly, larry };

  // range-based for loop over the vector-based list
  // Prints:
  // Hello Moe Howard!
  // Hello Larry Fine!
  // Hello Shemp Howard!
  for (Person p : vpl)
    cout << "Hello " << p.getFullName() << '!' << endl;

  // range-based for loop over the array-based list
  // Prints:
  // Hello Moe Howard!
  // Hello Curly Howard!
  // Hello Larry Fine!
  for (Person p : apl)
    cout << "Hello " << p.getFullName() << '!' << endl;
}

About the Author

Diego Dagum is a software architect and developer with more than 20 years of experience. He can be reached at email@diegodagum.com.