C ++:自动初始化

时间:2010-09-22 17:30:14

标签: c++ variables initialization declaration

我发现有时候我必须手动初始化所​​有POD类型。 E.g。

struct A {
    int x;
    /* other stuff ... */

    A() : x(0) /*...*/ {}
    A(/*..*/) : x(0) /*...*/ {}
};

我不喜欢这个有几个原因:

  • 我必须在每个构造函数中重做它。
  • 初始值与变量声明的位置不同。
  • 有时我必须实现构造函数的唯一原因是因为这个。

为了解决这个问题,我尝试使用自己的类型。即而不是使用int x,y;,我使用自己的矢量结构,它也使用0自动初始化。我还考虑过实现一些简单的包装器类型,例如:

template<typename T>
struct Num {
    T num;
    Num() : num(0) {}
    operator T&() { return num; }
    operator const T&() const { return num; }
    T& operator=(T _n) { num = _n; return num; }
    /* and all the other operators ... */
};

到目前为止,这基本上解决了我想用0进行初始化的所有情况(这是迄今为止最常见的情况)。

感谢James McNellis的提示:这也可以通过boost::value_initialized来解决。


现在,不限于POD类型:

但有时我想用不同的东西进行初始化,并且再次出现问题,因为Num模板结构不能轻易扩展以允许它。基本上是因为我无法将浮点数(例如float)作为模板参数传递。

在Java中,我会这样做:

class A {
    int x = 42;
    /*...*/

    public A() {}
    public A(/*...*/) { /*...*/ }
    public A(/*...*/) { /*...*/ }
    /*...*/
}

我觉得非常重要的是,在这种情况下,你想在所有可能的构造函数中始终以相同的方式初始化成员变量,你可以直接在成员变量旁边写入init值,就像在{{ 1}}。

所以我试图解决的问题是在C ++中做同样的事情。

为了解决我无法通过模板参数传递init值的问题,我将一个丑陋的宏一起攻击:

int x = 42;

(对于其他编译器,我可以省略结构的#define _LINENAME_CAT( name, line ) name##line #define _LINENAME( name, line ) _LINENAME_CAT( name, line ) /* HACK: use _LINENAME, workaround for a buggy MSVC compiler (http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=360628)*/ #define PIVar(T, def) \ struct _LINENAME(__predef, __LINE__) { \ typedef T type; \ template<typename _T> \ struct Data { \ _T var; \ Data() : var(def) {} \ }; \ Data<T> data; \ T& operator=(const T& d) { return data.var = d; } \ operator const T&() const { return data.var; } \ operator T&() { return data.var; } \ } 名称,只保留未命名。但是MSVC不喜欢它。)

这现在或多或少地像我想要的那样。现在它看起来像:

_LINENAME

虽然它做了我想要的(大部分),但我仍然不满意它:

  • 我不喜欢名字struct A { PIVar(int,42) x; /*...*/ A() {} A(/*...*/) { /*...*/ } A(/*...*/) { /*...*/ } /*...*/ }; (代表PIVar),但我真的无法想出更好的东西。与此同时,我希望简短。
  • 我不喜欢那个宏观黑客。

你是怎么解决这个问题的?有更好的解决方案吗?


有一个答案再次被删除,表示C ++ 0x允许基本上与Java相同的语法。真的吗?那么我只需要等待C ++ 0x。


请不要发表任何评论:

  • “然后只使用Java”/“不要使用C ++然后”或
  • “如果你需要这样的东西,你可能做错了什么”或
  • “不要这样做”。

另外,请不要告诉我不要使用它。我知道我当前解决方案的所有缺点。如果您确定我不知道这一点,请仅对非明显的缺点发表评论。请不要只说我当前的解决方案有许多缺点。另请注意,使用它并不会更糟。我只想问你是否知道一个比我在这里提出的更好的解决方案。

6 个答案:

答案 0 :(得分:10)

  

有时我必须实现构造函数的唯一原因是因为这个。

你不必这样做。

struct POD {
  int i;
  char ch;
};

POD uninitialized;

POD initialized = POD();

同样在初始化列表中:

class myclass
  POD pod_;
  // ....
  myclass()
   : pod_() // pod_'s members will be initialized
  {
  }

  

为了解决这个问题,我尝试使用自己的类型。

您的类型在此方案中失败:

void f(int&);

Num<int> i;
f(i);

可能会有更多问题,但这就是我立刻想到的。


  

你是怎么解决这个问题的?有更好的解决方案吗?

是的,我们都解决了这个问题。我们没有尝试对抗语言,而是按照创建方式使用它:初始化初始化列表中的POD。当我看到这个:

struct ML_LieroX : MapLoad {        
    std::string       id;
    PIVar(int, 0)     type;
    std::string       themeName;
    PIVar(int, 0)     numObj;
    PIVar(bool,false) isCTF;
我很畏缩。这是做什么的?为什么会这样?这甚至是C ++吗?

所有这些只是为了节省一些键入初始化列表的键击?你是认真的吗?

这是一个古老的好消息: 一段代码被写入一次,但在其生命周期内将被读取数十,数百甚至数千次。 这意味着从长远来看, 时,片段代码或多或少可以忽略。即使你花了十倍的时间来编写适当的构造函数,但它节省了我理解代码所需时间的10%,那么编写构造函数就是你应该做的。

答案 1 :(得分:7)

Boost提供了一个value_initialized<T>模板,可用于确保对象(PO​​D与否)进行值初始化。 Its documentation详细解释了使用它的利弊。

您对无法自动将对象初始化为给定值的投诉没有多大意义;与POD对象无关;如果要使用非默认值初始化非POD类型,则必须在初始化时指定该值。

答案 2 :(得分:2)

您可以按如下方式初始化POD结构:

struct POD
{
    int x;
    float y;
};

int main()
{
    POD a = {};            // initialized with zeroes
    POD b = { 1, 5.0f };   // x = 1, y = 5.0f

    return 0;
}

答案 3 :(得分:0)

proposal for C++0x允许这样:

struct A {
    int x = 42;
};

这正是我想要的。

如果这个提议没有进入最终版本,delegating constructors的可能性是至少避免在每个构造函数中重新编码初始化的另一种方式(同时避免使用虚拟辅助函数)这样做。)

在目前的C ++中,尽管我已经证明了这一点,但似乎还没有更好的方法。

答案 4 :(得分:0)

C ++确实有构造函数委托,为什么不使用它呢?

struct AState
{
    int x;
    AState() : x(42) {}
};

class A : AState
{
    A() {}
    A(/*...*/) { /*...*/ }
    A(/*...*/) { /*...*/ }
};

现在,x的初始化由所有构造函数委派。基础构造函数甚至可以接受从A::A的每个版本传递的参数。

答案 5 :(得分:0)

在C ++ 0x之前,如果要初始化的非零值不是完全任意的(实际情况通常如此),则有一个解决方案可以正常工作。类似于boost :: initialized_value但是有一个额外的参数来获取初始值(因为C ++会有点挑剔)。

template<typename T> struct Default { T operator()() { return T(); } };
template<typename T, T (*F)()> struct Call { T operator()() { return F(); } };
template<int N> struct Integer { int operator()() { return N; } };

template< typename X, typename Value = Default<X> >
class initialized {
public:
  initialized() : x(Value()()) {}
  initialized(const X& x_) : x(x_) {}
  const X& get() const { return x; }
  operator const X&() const { return x; }
  operator X&() { return x; }
private:
  X x;
};

您可以这样使用它:

struct Pi { double operator()() { return 3.14; } }; //Exactly
const char* init_message() { return "initial message"; }
Point top_middle() { return Point(screen_width()/2, 0); }

struct X {
  initialized<int> a;
  initialized<int, Integer<42> > b;
  initialized<double> c;
  initialized<double, Pi> d;
  initialized<std::string> e;
  initialized<std::string, Call<const char*, init_message> > f;
  initialized<Point> g;
  initialized<Point, Call<Point,top_middle> > h;
  X() {}
};

我发现必须创建一个虚函数以返回任何非整数/非默认值的烦恼通常在整个库中分摊(因为特定类型的非零初始值通常由许多类共享)。

显然,typedef是这里的朋友。

无论如何,迫不及待地升级到C ++ 0x / 11/14 /无论如何。