No offence or unfair stereotyping intended, but the only code-bases I've used that were based around two-phase construction, were projects where the majority of staff weren't very good at C++, but were decent at C, so they were more comfortable with
the old way of doing things.
Can control initialization and cleanup of object outside allocating space for the object. So if I wanted an array of objects that initialize with certain variables it doesn't get annoying if objects don't have default constructors.
C++ has a tool for this -- placement new (see below), which is the standard way to separate allocation from construction, and is what every container class will use internally to do so.
Can have a return result for success or failure. Little simpler than throwing an exception & catching it outside object construction.
In my experience, it's very rare to have classes that can fail construction. However, just because you don't have a return value doesn't mean you cant return a failure code. Use an out-argument (non-const pointer or reference argument) and write an error code to it.
Can call virtual functions within initialization.
Again, in my experience it's very rare to require virtual constructors, but if you've got a genuine use-case that requires them, and doesn't fit in the
typical C++ solutions, then two-phase might have a purpose there. Often these classes will use a factory function, so from the user's point of view there's still only one line of code to fully construct the object.
I'm a little confused about how could go about doing this...
Do you really have a use-case where you need an array with a very large, hard-coded size, where they're all constructed up-front?
The standard solution would be as follows, but yes, this won't work on non-copyable objects. However, on copyable objects, the theoretical initialize-temp/copy-construct/destruct-temp operation can be optimized down to just a regular construct operation per element. Also, C++11 adds the move-constructor, which allows you to have non-copyable classes that are still movable, so they can be moved into vectors instead of being copied into them.
std::vector<MyClass> vec(1000, iInitializeValue);
Or to construct them one at a time, you'd use something like below (
and again, although they're copied, if the copy operation has no side effects it will be optimized out):
std::vector<MyClass> vec;
vec.reserve(1000);
//...
vec.push_back(1337);
However, a large, hard-coded array sounds more like it's going to be used as a pool, which means the elements shouldn't be constructed until they're needed. If that's the case, then you're example is going to give you the wrong kinds of suggested answers.
Using placement-new, mentioned above, looks like this and separates allocation from construction:
MyClass* data = (MyClass*)malloc(sizeof(MyClass)*1000);//allocate array
MyClass* instanceAtIndex42 = new(data+42) MyClass(1337);//construct element at index 42
instanceAtIndex42->~MyClass();//destruct element at index 42
free(data);//deallocate array
However, instead of using placement-new directly, it's usually used inside container classes (like std::vector, or your own reinvention).
In my engine (
1,
2,
3), I use
scope-stack allocation, and I'd write your large array case like:
class MyThingThatOwnsAHugeArray
{
public:
MyThingThatOwnsAHugeArray(Scope& a, int size) : data(eiNewArray(a, MyClass, size)) {}
private:
MyClass* data;
};
//...
Scope a( stack );
MyThingThatOwnsAHugeHardCodedArray* bigThing = eiNew(a, MyThingThatOwnsAHugeHardCodedArray)(1000);
Although the array is dynamically allocated, it will exist sequentially in memory right after the parent allocation, so it's just as 'local' as if it were a hard-coded array like in your example; not in 'some random place'.
Alternatively, I'd use some kind of pool, like:
Pool<MyClass> myPool(a, 1000);//allocate space for 1000 items
//if the pool is designed to call constructors:
MyClass* item = myPool.Alloc(MyClass(1337));//construct one via copy constructor
myPool.Release(item);//destruct one
//or a POD pool:
MyClass* item = new(myPool.Alloc())MyClass(1337);//allocate one and then call constructor
item->~MyClass();//destruct
myPool.Release(item);//deallocate