未初始化的类中的数组

An array in a class without initialization

本文关键字:数组 初始化      更新时间:2023-10-16

这个问题我已经纠结很久了。在c++中,在类A中创建的类B数组必须通过默认构造函数初始化,这似乎是相当无效的。有什么方法可以避免这种行为吗?我实现了一个人员登记。如果我用count引用来创建它,我就会得到很多默认构造函数调用,而且它的效率似乎比应该的要低。我还必须创建默认构造函数,这是不必要的。

当您创建对象数组时,无论是静态数组(Person people[1000])还是动态分配数组(Person* people = new Person[1000]),所有1000个对象都将被创建并使用默认构造函数初始化。

如果你想为对象创建空间,但还没有创建它们,你可以使用像std::vector这样的容器(它实现了一个动态大小的数组),或者使用指针数组,比如Person* people[1000]Person** people = new Person*[1000]——在这种情况下,你可以用NULL初始化所有的项来指示空记录,然后一个一个地分配对象:people[i] = new Person(/* constructor arguments here */),但是你也必须记住单独delete每个对象。

我想我有你想要的解决方案。我在GCC 4.6上测试了这个,它可能需要修改msvc++的对齐位,但这里是示例输出和源代码:

源代码(用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   %08Xn", (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 %08Xn", (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 STARTn");
    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 ENDn");
    return 0;
}
样本输出:

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

首先,您不需要创建默认构造函数,否则编译器将生成其代码。我不认为有一种干净的方法可以避免在对象上调用默认构造函数(也许优化器会为数组剥离它),但肯定有一种肮脏的方法:

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

那么你仍然可以像使用固定大小的数组一样使用指针。sizeof和自增/自减将不起作用。

我想你不应该被默认构造函数的"低效"困扰太多。他们的存在是有原因的。否则,如果默认构造函数确实无事可做,则应该将其内联,这样就不会产生执行开销。

数组是怎样的,类B在A里面吗?它像B arr[size];吗?而是使用vector,这样你就可以在初始化时初始化大小,然后推入对象。或者像下面这样带有new的动态数组。initfunc可以创建你的注册。因为initfunc是在构造函数初始化时调用的,所以效率很高。

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.