用多个构造函数初始化C++成员变量

5
通常情况下,构造函数应该像这样:

//ctor1
SmallSim::SmallSim()
:mSimInit(false)
,mServersCreated(false)
,mTotalCPUTime(0)
{
    ...
}

如果我有多个构造函数会发生什么?

看起来如果我从第二个构造函数调用第一个构造函数,第一个构造函数中的成员变量不会被初始化。

//ctor2
SmallSim::SmallSim(bool ImmediateExecution, bool Report)
{
    SmallSim();

    ...
}

那么我需要在每个构造函数中重复使用 :mSimInit(false) ,mServersCreated(false) ,mTotalCPUTime(0) 吗?

据我所知,使用 InitClassVars() 不是最好的方法...

//ctor1
SmallSim::SmallSim()
{
    InitClassVars();

    ...
}

//ctor2
SmallSim::SmallSim(bool ImmediateExecution, bool Report)
{
    InitClassVars();

    ...
}

//Common function for init of member vars for multiple constructors
void SmallSim::InitClassVars(void)
{
    mSimInit = false;
    mServersCreated = false;
    mTotalCPUTime = 0;
}

有没有一种正确的方法可以在不重复初始化每个构造函数的情况下初始化成员变量?

3
你正在使用C++03还是C++11? - Electro
我正在使用gcc -std=c++0x编码,所以我认为11是可行的,因为智能指针可以工作。 - DieSlower
4个回答

11

如果您这样做:

SmallSim::SmallSim(bool ImmediateExecution, bool Report)
{
    SmallSim(); // 1

    ...
}

标记为1的行创建了一个新的SmallSim临时对象,因为它没有被使用,所以很快就会被销毁。它对当前正在初始化的对象没有任何影响。要在同一对象上调用另一个构造函数,可以按照以下方式进行:

SmallSim::SmallSim(bool ImmediateExecution, bool Report)
: SmallSim() {
    ...
}

(这是 C++11 的新特性。)


例子总是很有帮助的,谢谢。但现在我遇到了一个错误,它说“类型'SmallSim'不是'SmallSim'的直接基类”,你有什么想法吗?我的C++11标志已经打开了... - DieSlower
4
也许你的编译器还不支持委托构造函数。据我所知,至少需要 GCC 4.7 或 Clang 3.0 版本以上。 - R. Martinho Fernandes
1
@R.MartinhoFernandes,没错了!我现在使用的是gcc 4.6。谢谢! - DieSlower

6

在C++03中有两种选择:

  • 使用一个初始化函数,在每个构造函数中调用它
  • 为所有构造函数提供一个初始化列表

在C++11中,您可以使用委托构造函数,以便从所有其他构造函数中调用一个构造函数。


在每个构造函数中调用init时,必须小心。确保该方法不是虚拟的,因为在构造函数内调用虚拟方法并不总是会得到您期望的结果。 - J_D
@J_D:这当然是一个必须满足的要求,可以参考FAQ 23.5 http://www.parashift.com/c%2B%2B-faq-lite/strange-inheritance.html#faq-23.5(虽然在特殊情况下你可以使用虚函数,但需要非常小心谨慎)。! - dirkgently
1
@J_D 显然你只会从那个类初始化变量,所以我不知道你为什么期望一个虚函数在那里工作。 - Seth Carnegie
委托构造函数应该是答案。但现在我收到一个错误,说“类型'SmallSim'不是'SmallSim'的直接基类”,有什么想法吗?我的C++11标志已经打开了... - DieSlower

2
“没有正确的方法来做这件事。如果您将初始化委托给函数,则会默认初始化数据一次,然后再分配值。如果您想从初始化列表构造中获益,则必须重复自己。喜好因人而异。”
“幸运的是,C++11允许委托构造函数,详情请见此处。”
“C++11还允许在声明时进行初始化,这可能会有所帮助:”
struct Foo {
  int i{0};
  double x{0.}:
};

在这里,构造函数不必对i或进行任何操作,除非他们想要改变这些值。

0

正如@juanchopanza所提到的,处理构造函数没有“正确”或“完美”的方法,有几种分配对象和初始化其字段/属性的方法。

我不建议构造函数调用同一类中的另一个构造函数,这可能会导致混乱。

抱歉,我没有很好地理解您的示例,我将使用一个简单的示例。假设有一家商务酒店为旅行推销员提供自动化餐厅。

有一些机器可以接受订单,一些食物已经准备好了,例如咖啡、面包或蛋糕、果汁,而其他食物如华夫饼、鸡蛋需要时间来准备。

顾客可以到餐厅去机器,按下一些按钮点餐。他(她)可以要求服务员提供一些已经准备好的食物,例如咖啡,在等待其他食物时。他还可以拿一个空盘子等待所有食物一起上菜。

他可以稍后回来,以相同的价格再要更多的任何东西。

此示例将定义表示接受订单的机器的类。

请忽略小的语法错误和漏洞,并检查这个处理构造函数和字段初始化的想法。


// note: good idea to add an empty value to enumerations:

enum BeveragesEnum {
  none,
  water,
  orangejuice,
  milk,
  coffe,
  cocoa
}; // enum BeveragesEnum

enum BreadsEnum {
  none,
  donut,
  bagel,
  cherrypie,
  applepie,
  cake
}; // enum BreadsEnum

enum FoodEnum {
  none,
  ScrambledEggs,
  OverEggs,
  Chicken,
  Waffles,
  Pancakes,
  Beef
}; // enum FoodEnum

class AutomatedCafeteriaClass
  protected: 
    BeveragesEnum Beverage;
    BreadsEnum Bread;
    FoodEnum Food;

    public:
      /* constructor*/ AutomatedCafeteriaClass();
      /* destructor*/ ~AutomatedCafeteriaClass();

    public:
      void quickGiveMeCoffe();
      void wantCoffeAndBread
        (
          BreadsEnum aBread
        );
      void onlyBeverage(BeveragesEnum aBeverage);
      void BeverageBreadFirstFoodLater
        (
          BeveragesEnum aBeverage;
          BreadsEnum aBread;
          FoodEnum aFood;
        );
      void waitForEverything
        (
          BeveragesEnum aBeverage;
          BreadsEnum aBread;
          FoodEnum aFood;
        );
      void wantFood
        (
          FoodEnum aFood;
        );

    public:
      void prepare();
}; // class AutomatedCafeteriaClass

/* constructor*/ AutomatedCafeteriaClass::AutomatedCafeteriaClass()
{
  // first assign empty values
  this.Beverage = BeveragesEnum.none;
  this.Bread    = BreadsEnum.none;
  this.Food     = FoodsEnum.none;
} // constructor AutomatedCafeteriaClass

/* destructor*/ AutomatedCafeteriaClass::~AutomatedCafeteriaClass()
{
  // clear by assigning empty values
  this.Beverage = BeveragesEnum.none;
  this.Bread    = BreadsEnum.none;
  this.Food     = FoodsEnum.none;
} // constructor AutomatedCafeteriaClass

void AutomatedCafeteriaClass::quickGiveMeCoffe()
{
  this.Beverage = BeveragesEnum.Coffe;
  prepare();
}

void AutomatedCafeteriaClass::wantCoffeAndBread
(
BreadsEnum aBread
)
{
  this.Beverage = BeveragesEnum.Coffe;
  this.Bread    = aBread;
  prepare();
}

void AutomatedCafeteriaClass::onlyBeverage(BeveragesEnum aBeverage)
{
  this.Beverage = aBeverage;
  prepare();
}

void AutomatedCafeteriaClass::BeverageBreadFirstFoodLater
(
BeveragesEnum aBeverage;
BreadsEnum aBread;
FoodEnum aFood;
)
{
  this.Beverage = aBeverage;
  this.Bread    = aBread;
  this.Food     = aFood;
  prepare();
}

void AutomatedCafeteriaClass::waitForEverything
(
  BeveragesEnum aBeverage;
  BreadsEnum aBread;
  FoodEnum aFood;
)
{
  this.Beverage = aBeverage;
  this.Bread    = aBread;
  this.Food     = aFood;
  prepare();
}

void AutomatedCafeteriaClass::wantFood
(
  FoodEnum aFood;
)
{
  this.Food     = aFood;
  prepare();
}

void AutomatedCafeteriaClass::prepare();
{
  // prepare the food
}

请注意,所有这些函数虽然不是直接的构造函数,但它们像构造函数一样初始化字段,也称为“后置构造函数”或“初始化器”。
将所有这些函数转换为真正的构造函数也是可能的,但可能会有些混乱,因此更容易让每个函数都有自己的标识符。
干杯。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接