• Advertisement
Sign in to follow this  

array of a class without default constructors

This topic is 4713 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
Advertisement
Guest 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.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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;
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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);
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Sweet mother of...

Ouch. :) Nice technique though!

Share this post


Link to post
Share on other sites
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 only
void* buf = ::operator new(sizeof(foo)); // allocate only

foo* 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]).

Share this post


Link to post
Share on other sites
Guest 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I have one other question...

The following code compiles, but only initializes the first object. Is there any way to initialize the entire array? That's pretty much the entire point of this thread.


#include <new>
#include <iostream>
using namespace std;

class A
{
public:
A(const int src_x)
{
x = src_x;
}

~A()
{

}

int x;
};

int main(void)
{
void* buf = ::operator new(sizeof(A) * 50);

A* a = new(buf) A(50);

a->~A();

::operator delete(buf);

return 0;
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I guess one could use the = operator...

[source lang = "cpp"]
#include <new>
#include <iostream>
using namespace std;

class A
{
public:
A(const int src_x)
{
x = src_x;
}

~A()
{

}

A& A::operator=(const A& rhs)
{
if(&rhs == this)
return *this;

x = rhs.x;

return *this;
}

int x;
};

int main(void)
{
void* buf = ::operator new(sizeof(A) * 50);

A* a = new(buf) A(999);

for(size_t i = 0; i < 50; i++)
a = a[0];

cout << a[49].x;

a->~A();

::operator delete(buf);

return 0;
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by DigitalDelusion
Well you could simply borrow my code snippet and send your pointer and size instead of mine and it would work :p


I don't get your drift...

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Quote:
Original post by DigitalDelusion
Well you could simply borrow my code snippet and send your pointer and size instead of mine and it would work :p


I don't get your drift...


you could simply use the raw_init and raw_kill functions from eariler in this thread.

also using op= could potentially cause very bad things (tm) since that could have the not so nice sideeffect of invoking the dtor.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Another thing: how does one call the destructor on all of the objects in the array?

a->~A();

only destructs the first one.

for(size_t i = 0; i ~A();

is not legal.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by DigitalDelusion
Quote:
Original post by Anonymous Poster
Quote:
Original post by DigitalDelusion
Well you could simply borrow my code snippet and send your pointer and size instead of mine and it would work :p


I don't get your drift...


you could simply use the raw_init and raw_kill functions from eariler in this thread.

also using op= could potentially cause very bad things (tm) since that could have the not so nice sideeffect of invoking the dtor.


I see. Sorry, I was not paying close enough attention. Thanks for the info. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Another thing: how does one call the destructor on all of the objects in the array?

a->~A();

only destructs the first one.

for(size_t i = 0; i ~A();

is not legal.


well, you could have a look at the raw_kill function eariler in this thread..

or if you're lazy

for(size_t i = 0; i != N; ++i)
a.~A();


Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by DigitalDelusion
Quote:
Original post by Anonymous Poster
Another thing: how does one call the destructor on all of the objects in the array?

a->~A();

only destructs the first one.

for(size_t i = 0; i ~A();

is not legal.


well, you could have a look at the raw_kill function eariler in this thread..

or if you're lazy

for(size_t i = 0; i != N; ++i)
a.~A();



My mistake was using -> instead of . during that destruction loop. I should have checked your code first -- thanks :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
I see. Sorry, I was not paying close enough attention. Thanks for the info. :)


happens to all of us in the middle of the night I guess ;)

now go get a login instead of hiding behind that silly AP :p

Share this post


Link to post
Share on other sites
Here's another version that does not use placement new. Just another alternative:


[source code="cpp"]
#include <new>
#include <iostream>
using namespace std;

class A
{
public:
A(const int &src_x)
{
x = src_x;
}

int x;
};

int main(void)
{
size_t alloc_count = 50;

char *buf = new char[ (sizeof(A) * alloc_count) / sizeof(char) ];

A* a = reinterpret_cast<A*>(buf);

for(size_t i = 0; i < 50; i++)
a.x = i;

for(size_t i = 0; i < 50; i++)
cout << a.x << endl;

for(size_t i = 0; i < 50; i++)
a.~A();

delete buf;

return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by taby
Here's another version that does not use placement new. Just another alternative:


*** Source Snippet Removed ***


Sadly enough that's really not an option it just happens to work since it's a pod type they're not initialzed and their constructors haven't been called and dont even think about using op= since that would invoke the destructor possible throwing you into crash-land very fast.

Basicly that code is "programming by coincidence" on an even greater scale than my ugly hack was :) i suspect mine could run into alignment issues (but if you use new/malloc that aint a problem) but else it's actually quite safe.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement