C++:自动初始化

Alb*_*ert 3 c++ variables initialization declaration

我发现有时候我必须手动初始化所​​有POD类型.例如

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

    A() : x(0) /*...*/ {}
    A(/*..*/) : x(0) /*...*/ {}
};
Run Code Online (Sandbox Code Playgroud)

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

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

为了解决这个问题,我尝试使用自己的类型.即不使用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 ... */
};
Run Code Online (Sandbox Code Playgroud)

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

感谢James McNellis的提示:这也可以通过boost::value_initialized.


现在,不限于POD类型:

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

在Java中,我会这样做:

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

    public A() {}
    public A(/*...*/) { /*...*/ }
    public A(/*...*/) { /*...*/ }
    /*...*/
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

#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; } \
}
Run Code Online (Sandbox Code Playgroud)

(对于其他编译器,我可以省略_LINENAME该结构的名称,只是将其保留为未命名.但MSVC不喜欢它.)

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

struct A {
    PIVar(int,42) x;
    /*...*/

    A() {}
    A(/*...*/) { /*...*/ }
    A(/*...*/) { /*...*/ }
    /*...*/
};
Run Code Online (Sandbox Code Playgroud)

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

  • 我不喜欢这个名字PIVar(代表PreInitVar),但我真的无法想出更好的东西.与此同时,我希望简短.
  • 我不喜欢那个宏观黑客.

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


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


请不要给出任何评论:

  • "然后只使用Java代替"/"不要使用C++然后"或
  • "如果你需要这样的东西,你可能做错了什么"或者
  • "就是不要这样做".

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

sbi*_*sbi 10

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

你不必那样做.

struct POD {
  int i;
  char ch;
};

POD uninitialized;

POD initialized = POD();
Run Code Online (Sandbox Code Playgroud)

同样在初始化列表中:

class myclass
  POD pod_;
  // ....
  myclass()
   : pod_() // pod_'s members will be initialized
  {
  }
Run Code Online (Sandbox Code Playgroud)

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

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

void f(int&);

Num<int> i;
f(i);
Run Code Online (Sandbox Code Playgroud)

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


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

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

struct ML_LieroX : MapLoad {        
    std::string       id;
    PIVar(int, 0)     type;
    std::string       themeName;
    PIVar(int, 0)     numObj;
    PIVar(bool,false) isCTF;
Run Code Online (Sandbox Code Playgroud)

我畏缩.这是做什么的?为什么会这样?这甚至是C++吗?

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

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

  • @Albert:"在像Java这样的语言中"停在那里.抱歉,这不是Java.如果你想编写Java,那么就用Java编写C++编译器和程序.如果你想用C++编程,你需要用C++方式做事.希望C++很难成为Java是没有用的.C++没有办法成为Java.您需要编写类似于正常类的构造函数,编写命名构造函数(返回初始化对象的函数),保持未初始化并始终稍后进行初始化,或者使用那些令人讨厌的宏并穿上防火服. (4认同)
  • @Albert:我担心你错过了这一点.指着*任何*语言说"我希望我的语言能像语言一样"是一种糟糕的方法.您使用一种语言来解决问题,您不会使用它来解决问题,就像它是其他语言一样.拥抱C++,不要试图搞砸它,因为你不想写一个构造函数.每个用C++编程的人都会编写构造函数,这就是它的运作方式.您应该问"在C++中执行此操作的最佳方式是什么?",而不是"这是最好的方法,如何使其在C++中运行?". (3认同)
  • @Albert:这不是一个坏主意."让我们把宏观黑客写成排序,让这个好主意变得不那么有效"和"我们建议修改语言以正确支持它"之间存在很大的区别.至于你对`Num <T>`模板的尝试,请确保你阅读了我在答案中链接的Boost`value_initialized <T>`文档; 它讨论了如果要复制其功能需要考虑的一些注意事项和问题. (3认同)

Jam*_*lis 7

Boost提供了一个value_initialized<T>模板,可用于确保对象(PO​​D与否)进行值初始化. 它的文档详细解释了使用它的优缺点.

您对无法自动将对象初始化为给定值的投诉没有多大意义; 与POD对象无关; 如果要初始化非默认值的非POD类型,你有,当你把它初始化为指定的值.