构造函数初始化函数成员即时

时间:2017-06-06 15:55:54

标签: c++

MCVE

#include <map>
class A{
public:
    A(int){

    }
};

class B : public A{
public:
    B()
     : A(filter()){}
    int filter(){
        std::map<int,int> aStuff;
        //new(&m_aStuff)std::map<int,int>;
        m_aStuff = aStuff;
        return 0;
    }
private:
    std::map<int,int> m_aStuff;
};

int main(){
    B b;
    return 0;
}

这在编译时失败,因为m_aStuff未初始化。 使用answer我添加了

new(&m_aStuff)std::map<int,int>;

如果取消注释该行在编译时运行,但是当你离开过滤器类时,无论如何都会重新初始化m_aStuff。

2 个答案:

答案 0 :(得分:1)

我强烈建议您重新考虑设计,但这是我的解决方案:

class A {
public:
  A(int) {}
};

class B : public A {
public:
  struct Dirty {
    std::map<int, int> map;
  };

  B(Dirty dirt = Dirty()) : A(filter(dirt)), m_aStuff(std::move(dirt.map)) {}
  int filter(Dirty& dirt) {
    std::map<int, int> aStuff;
    //new(&m_aStuff)std::map<int,int>;
    dirt.map = aStuff;
    return 0;
  }

private:
  std::map<int, int> m_aStuff;
};

int main() {
  B b;
  return 0;
}

更好的解决方案:

class B : public A {
public:
  B() : B(filter()) {}

private:
  B(std::tuple<int, std::map<int, int>> t)
    : A(std::get<0>(t)), m_aStuff(std::move(std::get<1>(t))) {}

  static std::tuple<int, std::map<int, int>> filter() {
    std::map<int, int> aStuff;
    //new(&m_aStuff)std::map<int,int>;
    return make_tuple(0, aStuff);
  }    

  std::map<int, int> m_aStuff;
};

答案 1 :(得分:1)

作为你未提出的问题“如何修复它?” Yuki在没有任何细节的情况下回答我会提供一个答案“那里发生了什么?”。

在C ++中,对象是从基类构造的。所以订单如下:

A::A(int); // base class constructor
B::B(); // inherited class constructor

所有成员对象都在初始化列表中初始化:

B::B()
: // Initializer list begin.
A(), // Base class initialized first.
m_aStuff() // Members of current class are initialized next.
{ // End of initializer list.
    // All members are safe to use.
}

这是对象初始化的顺序。正如您在此处所见,成员m_aStuff在基础构造函数A::A(int)之后初始化。 构造函数是一个成员函数,因为每个函数都需要求值参数。因此在初始化对象之前调用函数int B::filter()。这意味着成员变量也未初始化。换句话说,执行顺序是:

  1. 致电B::filter()
  2. 修改成员变量B::m_aStuff
  3. 调用基类构造函数A::A(int)
  4. 初始化成员变量B::m_aStuff
  5. 显然,第2步是在初始化之前修改变量。根据{{​​1}}的实现,这可能会导致不同的行为(可能是未定义的)。

    事实上,以下两个结构是相同的:

    std::map

    B::B() :
    A(0)
    {}
    

    但在第二种情况下,您是显式初始化它,而在第一种情况下,编译器将为您生成此代码。

    您的解决方案B::B() : A(0), m_aStuff() {} 在使用之前初始化对象,这会使行为更加明确。但接下来生成的类new(&m_aStuff)std::map<int,int>;的构造函数将启动,B将再次初始化。这会将你的地图设置为初始状态(虽然我可以想象内存会泄漏的场景)。