非静态成员数组初始化有任何解决方法吗?

时间:2020-03-06 14:35:21  来源:igfitidea点击:

在C ++中,不可能在初始化列表中初始化数组成员,因此成员对象应该具有默认构造函数,并且应该在构造函数中正确地对其进行初始化。除了不使用数组之外,是否有任何(合理的)解决方法?

[在我们的应用程序中,只能使用初始化列表进行初始化的任何事情都比使用构造函数更可取,因为可以由编译器和链接器分配和初始化数据,并且每个CPU时钟周期都可以计数,甚至在main之前。但是,并非总是可以为每个类都使用默认构造函数,此外,在构造函数中再次重新初始化数据反而会破坏目的。

例如。我想要这样的东西(但是这个不起作用):

class OtherClass {
private:
    int data;
public:
    OtherClass(int i) : data(i) {}; // No default constructor!
};

class Foo {
private:
    OtherClass inst[3]; // Array size fixed and known ahead of time.
public:
    Foo(...)
        : inst[0](0), inst[1](1), inst[2](2)
        {};
};

我知道的唯一解决方法是非数组方法:

class Foo {
private:
    OtherClass inst0;
    OtherClass inst1;
    OtherClass inst2;
    OtherClass *inst[3];
public:
    Foo(...)
        : inst0(0), inst1(1), inst2(2) {
        inst[0]=&inst0;
        inst[1]=&inst1;
        inst[2]=&inst2;
    };
};

编辑:应该强调的是,OtherClass没有默认构造函数,并且非常希望使链接器能够使用堆分配所需的任何内存(将创建一个或者多个Foo静态实例)。本质上是僵尸。我已经更新了上面的示例以突出第一点。

解决方案

一种可能的解决方法是避免编译器完全调用OtherClass构造函数,并使用new放置自行调用它,以所需的任何方式对其进行初始化。例子:

class Foo
  {
  private:
    char inst[3*sizeof(OtherClass)]; // Array size fixed. OtherClass has no default ctor.

    // use Inst to access, not inst
    OtherClass &Inst(int i) {return (OtherClass *)inst+i;}
    const OtherClass &Inst(int i) const {return (const OtherClass *)inst+i;}
  public:
    Foo(...)
    {
      new (Inst(0)) OtherClass(...);
      new (Inst(1)) OtherClass(...);
      new (Inst(2)) OtherClass(...);
    }
    ~Foo()
    {
      Inst(0)->~OtherClass();
      Inst(1)->~OtherClass();
      Inst(2)->~OtherClass();
    }
  };

为了满足OtherClass可能的对齐要求,如果在VisualC ++中工作,则可能需要使用__declspec(align(x)),或者使用char以外的其他类型,例如:

Type inst[3*(sizeof(OtherClass)+sizeof(Type)-1)/sizeof(Type)];

...其中Type是int,double,long long或者描述对齐要求的任何内容。

OtherClass中有哪些数据成员?对于该类,值初始化是否足够?

如果值初始化足够,则可以在成员初始化列表中对数组进行值初始化:

class A {
public:
  A ()
  : m_a()  // All elements are value-initialized (which for int means zero'd)
  {
  }

private:
  int m_a[3];
};

如果数组元素类型是类类型,则将调用默认构造函数。

编辑:只是为了澄清来自Drealmer的评论。

如果元素类型是非POD,则它应具有"可访问的默认构造函数"(如上所述)。如果编译器无法调用默认构造函数,则此解决方案将不起作用。

下面的示例不适用于此方法:

class Elem {
public:
   Elem (int);  // User declared ctor stops generation of implicit default ctor
};

class A {
public:
  A ()
  : m_a ()         // Compile error: No default constructor
  {}

private:
  Elem m_a[10];
};

默认情况下,不初始化数组成员。因此,我们可以使用静态助手函数进行初始化,并将该助手函数的结果存储在成员中。

#include "stdafx.h"
#include <algorithm>
#include <cassert>

class C {
public: // for the sake of demonstration...
  typedef int t_is[4] ;
  t_is is;
  bool initialized;

  C() : initialized( false )
  {
  }

  C( int deflt )
    : initialized( sf_bInit( is, deflt ) )
  {}

  static bool sf_bInit( t_is& av_is, const int i ){
    std::fill( av_is, av_is + sizeof( av_is )/sizeof( av_is[0] ), i );
    return true;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{

  C c(1), d;

  assert( c.is[0] == 1 );

  return 0;
}

值得注意的是,在下一个标准中,它们将支持数组初始化程序。

使用继承创建代理对象

class ProxyOtherClass : public OtherClass {
public:   
  ProxyOtherClass() : OtherClass(0) {}
};

class Foo {
private:
  ProxyOtherClass inst[3]; // Array size fixed and known ahead of time.
public:
  Foo(...) {}
};

那么使用指针数组而不是对象数组呢?
例如:

class Foo {
private:
    OtherClass *inst[3];
public:
    Foo(...) {
        inst[0]=new OtherClass(1);
        inst[1]=new OtherClass(2);
        inst[2]=new OtherClass(3);
    };

    ~Foo() {
       delete [] inst;   
    }

};

我们说:"在我们的应用程序中,仅可以使用初始化列表进行初始化的任何事情都比使用构造函数更可取,因为可以由编译器和链接器分配和初始化数据,并且每个CPU时钟周期都可以计数"。

因此,不要使用构造函数。也就是说,不要使用常规的"实例"。静态声明所有内容。当我们需要一个新的"实例"时,可以在任何类之外创建一个新的静态声明。如果需要,请与公共成员一起使用结构。如有必要,请使用C。

你是在自问自答。构造函数和析构函数仅在具有大量分配和释放的环境中有用。如果目标是尽可能多地静态分配数据,那么破坏有什么好处,那么没有破坏的构造又有什么好处呢?与他们两个地狱。

我通常使用的一种使类成员"出现"在堆栈上的方法(尽管实际上存储在堆上):

class Foo {
private:
    int const (&array)[3];
    int const (&InitArray() const)[3] {
        int (*const rval)[3] = new int[1][3];
        (*rval)[0] = 2;
        (*rval)[1] = 3;
        (*rval)[2] = 5;
        return *rval;
    }
public:
    explicit Foo() : array(InitArray()) { }
    virtual ~Foo() { delete[] &array[0]; }
};

对于班级的客户,数组似乎是" int const [3]"类型。将此代码与new放置相结合,我们还可以使用所需的任何构造函数来真正地初始化值。希望这可以帮助。