array of a class without default constructors

Started by
20 comments, last by DigitalDelusion 19 years ago
I know when an array of a class is created the default constructor for that class and therefore when no default constructor is present a compile error occurs. Yet I know that the STL vector can create an array of objects without a default constructor. I also know it does this by using a prototype of the same class which it uses to default all the elements in the vector array. What I want to know is how is this done? I need to create an array of a class that cannot have a default constructor and without using the stl vector. Any ideas? thanks
Advertisement
Actually, the only reason why STL containers (say, vector) can be created using types with no default constructor is because it does not allocate memory to the internal pointer until you do a resize(), etc operation. The same occurs if you use the vector constructor which takes an initial size.

There is no way to do array initialization of objects using new. Only the default constructor is called, and if not available, it will not compile.
Quote:Original post by Anonymous Poster
The same occurs if you use the vector constructor which takes an initial size.


class A{public:	A(const int src_x)	{		x = src_x;	}	int x;};int main(void){	vector<A> a; // OK	vector<A> a(50); // bomb	vector<A> a(50, A(10)); // OK, because you are providing an object to use as the initializer	return 0;}
You can use an array of pointers to the object, or you can allocate raw memory and use placement new (Though this is dangerous and not recommended). If at all possible, prefer to give the object a default constructor.
Ok, this is a hack... a ugly one at that. But does the trick most of the time.

//beware... dragons ahead.#include <new>#include <iostream>struct A{	A(int x): x( x){ std::cout << "A::A()\n";}	A(const A &a): x( a.x){ std::cout << "A::A(A)\n";}	~A(void){ std::cout << "A::~A()\n";}	int x;};template <typename T>void raw_init( void *dst, size_t n, const T &prototype){	T *p( reinterpret_cast<T*>(dst));	for(size_t i = 0; i != n; ++i)		new (p++) A(prototype);}template <typename T>void raw_kill( T *p, size_t n){	for(size_t i = 0; i != n; ++i)		p++->~T();}int main(void){//	A a[10];//bombs//hold on to thy hat...	const int N = 10;	unsigned char data[sizeof(A) * N];//declare storage	raw_init( data, N, A(10));	A (&a)[N] = reinterpret_cast<A(&)[N]>(data);//quite horrid	//but from here we can use it just as if we would have declared our static array :)	//...	//dont forget to kill the whole mess to :)	raw_kill( a, N);}
HardDrop - hard link shell extension."Tread softly because you tread on my dreams" - Yeats
Sweet mother of...

Ouch. :) Nice technique though!
Quote:Original post by Anonymous Poster
Actually, the only reason why STL containers (say, vector) can be created using types with no default constructor is because it does not allocate memory to the internal pointer until you do a resize(), etc operation. The same occurs if you use the vector constructor which takes an initial size.

There is no way to do array initialization of objects using new. Only the default constructor is called, and if not available, it will not compile.


Your quite wrong [smile], the standard library containers are parameterized by allocator type, allocator concept seperates de/allocation and construction/destruction (very low-level not generally recommend unless your implementing containers or something similar). If you want to seperate it the general idea is:

#include <new>// or std::malloc, anything that allocates raw memory onlyvoid* buf = ::operator new(sizeof(foo)); // allocate onlyfoo* f = new(buf) foo();                 // construct only (placement new)                                         // (any constructor can be used including copy constructor)f->~foo();                               // destruct only                                         // you must do this with placement new::operator delete(buf);                  // deallocate only


Thus the technique can be extended to allocate a buffer/array to. The standard library also provides a set of algorithms that work with uninitialized memory aswell.

Note as already mentioned you could just create an array of pointers instead because the above technique is not recommended for general use (or just use the standard library containers [smile]).
My bad, I should not have said "the only reason". :)

The example still stands though... construction of the container only works when the container's default constructor is called.

How it allocates the memory is not really important.
Quote:Original post by Anonymous Poster
My bad, I should not have said "the only reason". :)

The example still stands though... construction of the container only works when the container's default constructor is called.

How it allocates the memory is not really important.


Well it actually is, and for an enlightening exercise ponder the differance between vector::resize and vector::reserve.
HardDrop - hard link shell extension."Tread softly because you tread on my dreams" - Yeats
Touche. Very good point, I did not think to check that.

When one writes their own allocator though, is this method of memory allocation required?

The reason why I am saying that it is unimportant is that the underlying implementation of memory allocation has nothing to do with the fact that if you create a container of objects with no default constructor, you must provide an example object that was manually constructed using whatever non-default constructor you choose.

Why else would the standard require this, other than to protect those who use allocators which do not rely on placement new?

This topic is closed to new replies.

Advertisement