0

I would like to allocate and array of classes on the stack without calling the constructor. The following example clarifys:

template<class t,int SetNum> class set
{
private:
    t Storage[SetNum];
};

class myClass
{
private:
    int* Array;
public:
    myClass()
    {
        Array=new int[10];
    }
}
int main()
{
    set<myClass,10> Set;
}

I do not want to allocate the 10 new ints for Array that occurs when myClass's constructor is called, but still want the space allocated for myClass.

Lauer
  • 517
  • 1
  • 6
  • 11
  • Question is ambiguous. Do you want it not allocate the array of myClass or the array of int? – garbagecollector Oct 23 '12 at 00:24
  • 1
    Creating an array of objects will automatically call the default constructor. There's no way to avoid this. What are you trying to do where you think you need to avoid calling the default constructor? – Code-Apprentice Oct 23 '12 at 00:25
  • @DumpHole The question clearly states the "I do not want to allocate the 10 new ints for `Array` that occurs when `myClass`'s constructor is called" – Code-Apprentice Oct 23 '12 at 00:25
  • 2
    @Code-Guru He was asking "without calling the constructor" at the beginning. So I'm confused whether the question is to prevent the constructor from being called, or the array of int from being allocated. – garbagecollector Oct 23 '12 at 00:28

1 Answers1

1

You have to have an array of unsigned chars (or such) to use as "backing storage" for your elements, and then call the placement new operator (see e.g. here) to construct your instances there (which, by the way, is what std::vector already does).

Warning: if you use placement new you have the responsibility to deallocate manually the objects you created with it, calling explicitly the destructor; also, the pointer you pass to placement new must be properly aligned for the objects you are creating, otherwise bad stuff may happen.

See also this question.


Example of a twisted mix of std::array and std::vector built with the techniques described (requires C++11 for the union trick to work):

#include <cstddef>
#include <memory>
#include <stdexcept>
#include <iostream>

template<typename T, std::size_t N>
class array_noinit
{
    union storage_helper
    {
    private:
        // this member assures that the storage is properly aligned
        T t;
    public:
        unsigned char storage[sizeof(T)*N];

        // needed because T's constructor/destructor is implicitly deleted
        storage_helper() { };
        ~storage_helper() { };
    };

    storage_helper s;

    std::size_t _size;
    T * _storage;
public:
    array_noinit() :
        _size(0), _storage((T*)s.storage)
    {}

    ~array_noinit()
    {
        while(_size>0)
            pop_back();
    }

    void push_back(const T & elem)
    {
        if(_size>=N)
            throw std::runtime_error("Not enough capacity.");
        new(_storage+_size) T(elem);
        _size++;
    }

    void pop_back()
    {
        if(_size>0)
        {
            _size--;
            _storage[_size].~T();
        }
    }

    T & at(std::size_t Idx)
    {
        if(Idx>=_size)
            throw std::out_of_range("Idx out of range.");
        return _storage[Idx];
    }

    std::size_t size() const
    {
        return _size;
    }

    std::size_t capacity() const
    {
        return N;
    }
};

class A
{
    int _i;
public:
    A(int i) : _i(i)
    {
        std::cout<<"In A constructor - "<<_i<<"\n";
    }

    A(const A & right)
        : _i(right._i)
    {
        std::cout<<"In A copy constructor - "<<_i<<"\n";
    }

    ~A()
    {
        std::cout<<"In A destructor - "<<_i<<"\n";
    }
};

int main()
{
    std::cout<<"With ints\n";
    array_noinit<int, 4> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(4);

    for(std::size_t i=0; i<4; i++)
        std::cout<<arr.at(i)<<" ";
    std::cout<<"\n";

    std::cout<<"With a class - we don't add anything\n";
    array_noinit<A, 10> arr2;

    std::cout<<"With a class - now we add stuff\n";
    array_noinit<A, 10> arr3;
    arr3.push_back(A(1));
    arr3.push_back(A(2));
    arr3.push_back(A(3));
    return 0;
}

Output:

With ints
1 2 3 4 
With a class - we don't add anything
With a class - now we add stuff
In A constructor - 1
In A copy constructor - 1
In A destructor - 1
In A constructor - 2
In A copy constructor - 2
In A destructor - 2
In A constructor - 3
In A copy constructor - 3
In A destructor - 3
In A destructor - 3
In A destructor - 2
In A destructor - 1

Edit there's a much simpler way to get aligned storage.

Community
  • 1
  • 1
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • Not every object can be placed at any address, some have particular restrictions about the address where they can be placed (e.g. on some platforms 32 bit integers can stay only on addresses exactly multiple of 4 bytes). To solve this kind of problem, I'd have the "backing storage" `sizeof(t)` larger than needed, and then use the `alignof` operator/`align` function to determine the first address for actual use as storage for objects of type `t`. – Matteo Italia Oct 23 '12 at 00:37
  • ... actually, you could use another trick: your "backing storage" could stay inside a `union` with a single element of type `t`, which isn't automatically constructed/destructed (C++11, [class.union] ¶2-4), but guarantees that it as a whole is aligned correctly for your type `t`. – Matteo Italia Oct 23 '12 at 00:44
  • There is a problem that classes with constructors cant be used in unions. – Lauer Oct 23 '12 at 12:11
  • @user1204406: that restriction has been lifted in C++11, that's why I said several times that such code requires C++11 to compile correctly. – Matteo Italia Oct 23 '12 at 12:11