user1890078
user1890078

Reputation: 217

An array in a class without initialization

I have been struggling with that issue long time. It seems to be quite ineffective in C++ that array of class B created in class A have to be initialized by default constructor. Is there any way to avoid that behavior ? I implement a register of people. If I create it with count references, I get a lot of default constructor callings and it seems to be less effective that it should be. Also I have to create default constructor which is unnecessary.

Upvotes: 2

Views: 2457

Answers (4)

Forest Darling
Forest Darling

Reputation: 311

I think I have the solution you are aiming for. I tested this on GCC 4.6 and it may require modification for MSVC++ for the alignment bit, but here is the sample output and the source code:

Source Code (tested with GCC 4.6):

#include <cstdio>
#include <cstring>
#include <new>

// std::alignment_of

template <typename T, std::size_t capacity>
class StaticVector
{
public:
    StaticVector() : _size(0)
    {
        // at this point we've avoided actually initializing
        // the unused capacity of the "static vector"
    }
    ~StaticVector()
    {
        // deconstruct in reverse order of their addition
        while (!empty())
            pop_back();
    }
    void push_back(const T &src)
    {
        // at this point we initialize the unused array entry by copy
        // constructing it off the passed value
        new (data() + _size) T(src);
        _size++;
    }
    void pop_back()
    {
        _size--;
        // we manually call the deconstructor of the entry we are marking as unused
        data()[_size].~T();
    }
    bool empty() const {return _size == 0;}
    std::size_t size() const {return _size;}

    // NOTE: you'd better index only into constructed data! just like an std::vector
    T & operator[](int i) {return *data()[i];}
    const T & operator[](int i) const {return *data()[i];}
    T * data() {return reinterpret_cast<T*>(_data);}
    const T * data() const {return reinterpret_cast<const T*>(_data);}
protected:
// NOTE: I only tested this on GCC 4.6, it will require some
// conditional compilation to work with MSVC and C++11
#if 1 // for GCC without c++11
    char _data[sizeof(T[capacity])] __attribute__((aligned(__alignof__(T))));
#else // UNTESTED: The C++11 way of doing it?
    alignas(T) char _data[sizeof(T[capacity])]; // create a suitable sized/aligned spot for the array
#endif
    std::size_t _size;
};

// NOTE: lacks a default constructor, only
// constuctor that takes parameters
class B
{
public:
    B(int param1, const char param2[])
    {
        printf("Constructing   B at   %08X with parameters (%i, %s)\n", (int)this, param1, param2);
        x = param1;
        strcpy(buffer, param2);
    }
    ~B()
    {
        printf("Deconstructing B at   %08X\n", (int)this);
    }
    // NOTE: only provided to do the printf's, the default
    // copy constructor works just fine
    B(const B &src)
    {
        printf("Copying        B from %08X to %08X\n", (int)(&src), (int)this);
        x = src.x;
        memcpy(buffer, src.buffer, sizeof(buffer));
    }
protected:
    int x;
    char buffer[128];
};

class A
{
public:
    StaticVector<B, 8> staticVectorOfB;
};

int main()
{
    printf("PROGRAM START\n");
    A a;
    a.staticVectorOfB.push_back(B(0, "Uno"));
    a.staticVectorOfB.push_back(B(1, "Dos"));
    a.staticVectorOfB.push_back(B(2, "Tres"));
    printf("PROGRAM END\n");
    return 0;
}

Sample Output:

PROGRAM START
Constructing   B at   0022FDC4 with parameters (0, Uno)
Copying        B from 0022FDC4 to 0022F9A0
Deconstructing B at   0022FDC4
Constructing   B at   0022FE48 with parameters (1, Dos)
Copying        B from 0022FE48 to 0022FA24
Deconstructing B at   0022FE48
Constructing   B at   0022FECC with parameters (2, Tres)
Copying        B from 0022FECC to 0022FAA8
Deconstructing B at   0022FECC
PROGRAM END
Deconstructing B at   0022FAA8
Deconstructing B at   0022FA24
Deconstructing B at   0022F9A0

Upvotes: 1

Abhijit-K
Abhijit-K

Reputation: 3679

How is the array, is class B inside A? Is it like B arr[size];? Instead use vector so that you can init the size in initialization and then push objects. Or dynamic array with new like below. The initfunc can create you register. Since the initfunc is called in initialization of the constructor it will be efficient.

class B { };

class A
{
    B *barray;
    B* initfunc()
    {
        B* tmp = new B[5];

        //init elements of B

        return tmp;
    }
public:
    A():barray(initfunc())
    {

    }

    ~A()
    {
        delete[] barray;
    }
};
//the code is not exception safe, vector recommended.

Upvotes: 0

Roman Saveljev
Roman Saveljev

Reputation: 2594

First, you do not need to create default constructor, because otherwise the compiler will generate its code. I do not think there is a clean way to avoid calling default constructor on the object (perhaps optimizer would strip it out for the array), but there is surely a dirty one:

class B
{
};

class A
{
private:
    char _array[sizeof(B)*5];
    B* getB() {return (B*)_array;}
};

Then you can still use the pointer the same way as you would use fixed size array. sizeof and increment/decrement will not function though.

I guess you should not be bothered too much by "inefficiences" from default constructor. They are there for a reason. Otherwise, if default constructor really has no job to do, it should be inlined and then it will generate no overhead to execution.

Upvotes: 0

riv
riv

Reputation: 7323

When you are creating an array of objects, be it a static array (Person people[1000]) or dynamically allocated (Person* people = new Person[1000]), all 1000 objects will be created and initialized with the default constructor.

If you want to create space for the objects, but not create them just yet, you can either use a container like std::vector (which implements a dynamically sized array), or use an array of pointers, like Person* people[1000] or Person** people = new Person*[1000] - in this case, you can initialize all items with NULL to indicate empty records, and then allocate objects one by one: people[i] = new Person(/* constructor arguments here */), but you will also have to remember to delete each object individually.

Upvotes: 1

Related Questions