简体   繁体   English

类中没有初始化的数组

[英]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. 在C ++中,必须由默认构造函数初始化在类A中创建的类B的数组似乎是无效的。 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. 如果我使用count引用创建它,则会得到很多默认的构造函数调用,并且似乎效率不高。 Also I have to create default constructor which is unnecessary. 另外,我必须创建不必要的默认构造函数。

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. 当创建对象数组时,无论是静态数组( Person people[1000] )还是动态分配的( Person* people = new Person[1000] ),将创建所有1000个对象,并使用默认构造函数对其进行初始化。

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. 如果要为对象创建空间,但又不想立即创建它们,则可以使用std :: vector之类的容器(实现动态大小的数组),也可以使用指针数组,例如Person* people[1000]Person** people = new Person*[1000] -在这种情况下,您可以使用NULL初始化所有项目以指示空记录,然后一一分配对象: people[i] = new Person(/* constructor arguments here */) ,但是您还必须记住分别delete每个对象。

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: 我在GCC 4.6上对此进行了测试,可能需要对MSVC ++进行修改以使对齐位保持不变,但这是示例输出和源代码:

Source Code (tested with GCC 4.6): 源代码(已通过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

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. sizeof和增量/减量将不起作用。

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. 否则,如果默认构造函数确实没有工作要做,则应对其进行内联,然后不会产生执行开销。

How is the array, is class B inside A? 数组如何,B类在A内? Is it like B arr[size];? 像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. initfunc可以创建您的注册。 Since the initfunc is called in initialization of the constructor it will be efficient. 由于在构造函数的初始化中调用了initfunc,它将非常有效。

class B { }; 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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM