在C ++中模拟静态构造函数?

时间:2009-09-07 21:18:39

标签: c++ syntax

无论如何,我可以修改此代码示例

#include <stdlib.h>
#include <iostream>

class Base {
public:
    Base() {
        if(!m_initialized) {
            static_constructor();
            m_initialized = true;
        }
    }
protected:
    virtual void static_constructor() {
        std::cout << "Base::static_constructor()\n";
    }
private:
    static bool m_initialized;
};

bool Base::m_initialized = false;

class Derived : public Base {
    void static_constructor() {
        std::cout << "Derived::static_constructor()\n";
    }
};

int main(int argc, char** argv) {
    Derived d;
    return(EXIT_SUCCESS);
}

这样Derived::static_constructor()被调用而不是Base?我想初始化一堆静态变量,最合乎逻辑的地方是在类中的某个地方。

4 个答案:

答案 0 :(得分:5)

你永远不应该从构造函数(或析构函数)调用虚函数!结果将不是“预期的”(因此您看到的结果)。为什么?因为在Derived构造函数之前调用了基础构造函数(Base)。这意味着虚拟函数可能引用的Derived中的本地数据库尚未初始化。此外,甚至可能更重要的是,vtable尚未使用Derived中的函数进行初始化,仅使用Base的成员。因此,虚函数实际上并不是虚拟的 - 它不会直到Base()完成并且Derived()被处理。

另外,执行此操作会破坏Open/Closed-principle,简而言之,“类应该打开以进行扩展,但关闭以进行修改”。您正在通过更改Base静态初始化来尝试修改其行为而不是扩展它。这在当时看起来似乎是一个好主意,但稍后它可能会咬你的屁股;)

答案 1 :(得分:4)

我从Martin V Lowis的解决方案中采用了这个解决方案。主要区别在于它使用多重继承和CRTP:

template<class T>
class StaticInitializer : public T
{
  static bool initialized;
 public:
  StaticInitializer(){
    if(!initialized){
      T::static_constructor();
      initialized=true;
    }
  }
};

template<class T> bool StaticInitializer<T>::initialized;

class Base : public StaticInitializer<Base>
{
public:
  static void static_constructor() {
    std::cout << "Base::static_constructor()\n";
  }
};
static Base _base;

class Derived : public Base, public StaticInitializer<Derived>
{
public:
    static void static_constructor() {
      std::cout << "Derived::static_constructor()\n";
    }
};
static Derived _derived;

StaticInitializer的每个具体子类都获得了它自己的静态构造函数初始化方法,但是你保持了真正继承的优势。

答案 2 :(得分:3)

您可以避免使用模板类重复布尔变量。声明模板的实例,其构造函数将运行静态初始化程序。使实例成为静态,以便包含头文件将自动声明一个静态对象。

#include <iostream>
using namespace std;

template<class T>
class StaticInitializer{
  static bool initialized;
 public:
  StaticInitializer(){
    if(!initialized){
      T::static_constructor();
      initialized=true;
    }
  }
};

template<class T> bool StaticInitializer<T>::initialized;

class Base{
public:
  static void static_constructor() {
    std::cout << "Base::static_constructor()\n";
  }
};
static StaticInitializer<Base> _base;

class Derived{
public:
    static void static_constructor() {
      std::cout << "Derived::static_constructor()\n";
    }
};
static StaticInitializer<Derived> _derived;

int main()
{}

答案 3 :(得分:0)

我建议这个解决方案:

#include <cstdlib>
#include <iostream>

class Object {
public:
    Object() {
        std::cout << m_var << std::endl;
    }
private:
    static int m_var, init_var();
};

int Object::init_var() {
    return 5;
}

int Object::m_var = Object::init_var();

int main(int argc, char** argv) {
    Object o;
    return(EXIT_SUCCESS);
}

这种方式m_var只被初始化一次,并且我将所有构造代码保留在类中。