非静态成员数组初始化的任何变通方法?

时间:2008-09-23 09:23:51

标签: c++

在C ++中,无法在初始化列表中初始化数组成员,因此成员对象应该具有默认构造函数,并且应该在构造函数中正确初始化它们。除了不使用数组之外,有没有(合理的)解决方法?

[只使用初始化列表可以初始化的任何东西都在我们的应用程序中远比使用构造函数更好,因为这些数据可以由编译器和链接器分配和初始化,并且每个CPU时钟周期都会计算,甚至在{{{ 1}}。但是,并不总是有可能为每个类都有一个默认构造函数,此外,在构造函数中再次重新初始化数据反而失败了目的。]

E.g。我想要这样的东西(但这个不起作用):

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,使用堆本质上是 verboten 。我已经更新了上面的例子,以突出第一点。

7 个答案:

答案 0 :(得分:4)

一种可能的解决方法是避免编译器根本调用OtherClass构造函数,并使用placement 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或描述对齐要求的任何内容。

答案 1 :(得分:2)

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];
};

答案 2 :(得分:1)

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

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]”类型。将此代码与placement new结合使用,您还可以使用您想要的任何构造函数自行决定初始化值。希望这会有所帮助。

答案 3 :(得分:0)

默认情况下,不会初始化数组成员。因此,您可以使用执行初始化的静态帮助函数,并将辅助函数的结果存储在成员中。

#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;
}

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

答案 4 :(得分: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(...) {}
};

答案 5 :(得分:0)

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

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;   
    }

};

答案 6 :(得分:0)

你说“在我们的应用程序中使用初始化列表初始化的任何东西都比使用构造函数要好得多,因为这些数据可以由编译器和链接器分配和初始化,并且每个CPU时钟周期都会计算”。 / p>

所以,不要使用构造函数。也就是说,不要使用传统的“实例”。静态地声明一切。当您需要一个新的“实例”时,创建一个新的静态声明,可能在任何类之外。如果必须,请使用公共成员的结构。如果必须,请使用C.

你回答了自己的问题。构造函数和析构函数仅在具有大量分配和释放的环境中有用。如果目标是尽可能多地静态分配数据,那么破坏有什么用呢?那么没有破坏的建设有什么好处呢?对他们两个都很痛苦。