C++: Multiple Distinct Objects


Create a sequence (array, list, whatever) consisting of n distinct, initialized items of the same type. n should be determined at runtime.

By distinct we mean that if they are mutable, changes to one do not affect all others; if there is an appropriate equality operator they are considered unequal; etc. The code need not specify a particular kind of distinction, but do not use e.g. a numeric-range generator which does not generalize.

By initialized we mean that each item must be in a well-defined state appropriate for its type, rather than e.g. arbitrary previous memory contents in an array allocation. Do not show only an initialization technique which initializes only to “zero” values (e.g. calloc() or int a[n] = {}; in C), unless user-defined types can provide definitions of “zero” for that type.

This task was inspired by the common error of intending to do this, but instead creating a sequence of n references to the same mutable object; it might be informative to show the way to do that as well, both as a negative example and as how to do it when that’s all that’s actually necessary.

This task is most relevant to languages operating in the pass-references-by-value style (most object-oriented, garbage-collected, and/or ‘dynamic’ languages).

By default C++ has value semantics, so this problem does not present itself unless the programmer deliberately choses to refer to objects though a pointer. Examples are given for both cases.
Using only language primitives:

// this assumes T is a default-constructible type (all built-in types are)
T* p = new T[n]; // if T is POD, the objects are uninitialized, otherwise they are default-initialized

//If default initialisation is not what you want, or if T is a POD type which will be uninitialized
for(size_t i = 0; i != n; ++i)
p[i] = make_a_T(); //or some other expression of type T

// when you don't need the objects any more, get rid of them
delete[] p;

Using the standard library

#include <vector>
#include <algorithm>
#include <iterator>

// this assumes T is default-constructible
std::vector<T> vec1(n); // all n objects are default-initialized

// this assumes t is a value of type T (or a type which implicitly converts to T)
std::vector<T> vec2(n, t); // all n objects are copy-initialized with t

// To initialise each value differently 
std::generate_n(std::back_inserter(vec), n, makeT); //makeT is a function of type T(void)

In C++ reference semantics are achieved by holding objects by pointer. Here is an example of the error, and a correct way of achieving distinctness.
These examples assume T has a public copy constructor, and that p is a pointer to T;

#include <vector>
#include <tr1/memory>
using namespace std;
using namespace std::tr1;

typedef shared_ptr<T> TPtr_t;
// the following is NOT correct:
std::vector<TPtr_t > bvec_WRONG(n, p); // create n copies of p, which all point to the same opject p points to.

// nor is this:
std::vector<TPtr_t> bvec_ALSO_WRONG(n, TPtr_t(new T(*p)) ); // create n pointers to a single copy of *p

// the correct solution
std::vector<TPtr_t > bvec(n);
for (int i = 0; i < n; ++i)
bvec[i] = TPtr_t(new T(*p); //or any other call to T's constructor

// another correct solution
// this solution avoids uninitialized pointers at any point
std::vector<TPtr_t> bvec2;
for (int i = 0; i < n; ++i)
bvec2.push_back(TPtr_t(new T(*p)); 

Of course, also in this case one can use the other sequence containers or plain new/delete instead of vector.


Content is available under GNU Free Documentation License 1.2.