什么是静态构造函数?

78

这个问题是在面试中问我的:

什么是静态构造函数?

它在C++中存在吗?如果存在,请用一个例子来解释它。


20
看起来你错过了 C++ 的面试,参加了 Java/C# 的面试 ;) - Alok Save
可能是为什么C++没有静态构造函数的理由?的重复问题。 - sbi
1
如果针对C++程序员,应该解释而不是询问什么是静态构造函数,因为这不是C++术语。例如,它可能会询问构造函数是否可以具有静态链接,或者是否可以使用static关键字作为前缀来实现未指定的行为... - Tony Delroy
1
可能是为什么C++中的构造函数不能声明为静态的?的重复问题。 - D Mehta
13个回答

96

C++没有静态构造函数,但是您可以使用嵌套类的静态实例来模拟它们。

class has_static_constructor {
    friend class constructor;

    struct constructor {
        constructor() { /* do some constructing here … */ }
    };

    static constructor cons;
};

// C++ needs to define static members externally.
has_static_constructor::constructor has_static_constructor::cons;

1
我猜class constructor应该是class has_static_constructor的友元才能有用?否则它只是另一个静态成员。 - davka
@davka 评论不错。事实上,嵌套并没有特别有用(除了限制范围,这总是一件好事)。 - Konrad Rudolph
1
根据§7.1.5.3,是可以的。但GCC却把它退回给我了。 - Konrad Rudolph
13
在非本地静态对象构造函数中放置的内容需要非常小心。该构造函数的运行是不确定的,而且它会在启动过程的非常早期运行。Scott Meyer的《Effective C++》(第二版的第47项)对NLSO初始化进行了详细解释。这可能会在嵌入式世界中给您带来麻烦,在那里RTOS在构造函数运行时不可用。 - Tod
2
@Tod 非常有价值的评论。不幸的是,使用惰性加载会更加复杂。基本上,我会通过一个静态的 unique_ptr 解决这个问题,它指向一个嵌套类,该类将所有“静态”成员作为实际上的非静态成员,并且在初始化为 0 并首次访问时重置为有效指针。 - Konrad Rudolph
显示剩余4条评论

20

C++中没有静态构造函数。在C#中(可能在Java中也是如此),您可以定义静态构造函数,它会被运行时自动调用以初始化静态成员。

如果您有进一步的问题和兴趣,可以阅读此主题:

为什么C++中没有静态构造函数?


10

由于在C++中技术上不存在静态构造函数,您必须决定是否值得做一些花招来强制执行(例如使用嵌套类的静态实例),或者只需稍微重新组织代码,在程序运行早期调用静态初始化器。

#include <iostream>           // cout, endl

class Foo {
   public:
      static int s_count;

      // Constructor definition
      Foo (int l, int w, int h)
      {
         cout <<"Foo ctor called." << endl;
         length = l;
         width  = w;
         height = h;

         // Increase every time object is created
         s_count++;
      }

      int vol ()
      {
         return length * width * height;
      }

      static void initCount()
      {
         s_count = 0;
      }

      static int getCount()
      {
         return s_count;
      }

   private:
      double length;     // Length of a box
      double width;      // Width  of a box
      double height;     // Height of a box
};

// Initialize static member of class Foo
int Foo::s_count;  // Initializing here is non-deterministic

int main(void) {

   Foo::initCount();  // Initializing here is deterministic

   // Print total number of objects before creating object.
   cout << "Inital Count: " << Foo::getCount() << endl;

   Foo Foo1(3, 1, 1);    // Declare box1
   Foo Foo2(8, 6, 2);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Count: " << Foo::getCount() << endl;

   return 0;
}

Output:

$ static_init_test
Inital Count: 0
Foo ctor called.
Foo ctor called.
Final Count: 2

我更喜欢这种方法;好的一面是,它使非确定性初始化变得可行。

但是还有一点需要注意 -- 如果你需要初始化静态常量变量,这种技术是不够的。对于静态常量变量,你必须将它们设置为类中的私有成员,并提供 getter 方法让外部读取。

注意:我更新了这段代码 -- 它通过以下方式编译和成功运行,没有出现警告:

g++ static_init_test.cpp -std=c++11 -o static_init_test

1
这绝对是关于如何/为什么/是否/何时在C++中使用一些技巧来模拟静态构造函数的永恒问题的最佳答案。 - jose.angel.jimenez
@bearvarine 关于你最近在Meta-SO上的问题。是的,这是一个现在在SO上无法生存的问题之一。我每天都会投票关闭许多这种形式的问题,因为我认为它们对未来的研究者的价值很小(或者已经有重复的问题了),而且提问者在提问之前没有进行过研究。随着答案已经普遍可得,管理的口气已经发生了变化,特别是对于这种类型的问题。 - πάντα ῥεῖ
这导致在VS2017中出现以下错误: Severity Code Description Project File Line Suppression State LNK2001 unresolved external symbol "public: static class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > Foo::my_text_str" (?my_text_str@Foo@@2V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@A) - Harsh Kumar Narula
我刚遇到了这个问题。而且你的解决方案在现实世界的项目中并不总是可行的,因为你可能没有从头开始独立编写代码。 - Alex D

8

在C#和Java中存在静态构造函数。
它们用于初始化类的静态成员。
运行时会在类首次使用之前执行它们。


4

C++ 中不存在这样的东西。构造函数和析构函数通常用于创建或销毁对象实例。在没有对应对象实例的情况下调用它们是没有意义的。您可以使用 singleton 模式来模拟它们。


3

静态构造函数用于初始化类的静态数据。C++没有静态构造函数。但是可以通过使用友元类或嵌套类来模拟静态构造函数,如下所示。

class ClassStatic{
private:
    static char *str;
public:
    char* get_str() { return str; }
    void set_str(char *s) { str = s; }
    // A nested class, which used as static constructor
    static class ClassInit{
    public:
        ClassInit(int size){ 
            // Static constructor definition
            str = new char[size];
            str = "How are you?";
        }
    } initializer;
};

// Static variable creation
char* ClassStatic::str; 
// Static constructor call
ClassStatic::ClassInit ClassStatic::initializer(20);

int main() {
    ClassStatic a;
    ClassStatic b;
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    a.set_str("I am fine");
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    std::cin.ignore();
}

输出:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine

2
也许他们的意思是:
class Cat
{
private:
Cat();
public:
static Cat getCat() {return Cat(); }
}

1
我认为静态构造函数用于初始化任何静态数据,或执行只需要执行一次的特定操作。它会在创建第一个实例或引用任何静态成员之前自动调用。在 C++ 中,我们没有称为静态构造函数的东西,但是您可以模仿静态构造函数的功能。看看这个 C# 静态构造函数:
public class Bus  {
     // Static variable used by all Bus instances.
     // Represents the time the first bus of the day starts its route.
     protected static readonly DateTime globalStartTime;

     // Property for the number of each bus.
     protected int RouteNumber { get; set; }

     // Static constructor to initialize the static variable.
     // It is invoked before the first instance constructor is run.
     static Bus()
     {
         globalStartTime = DateTime.Now;

         // The following statement produces the first line of output, 
         // and the line occurs only once.
         Console.WriteLine("Static constructor sets global start time to {0}",
             globalStartTime.ToLongTimeString());
     }

     // Instance constructor.
     public Bus(int routeNum)
     {
         RouteNumber = routeNum;
         Console.WriteLine("Bus #{0} is created.", RouteNumber);
     }

     // Instance method.
     public void Drive()
     {
         TimeSpan elapsedTime = DateTime.Now - globalStartTime;

         // For demonstration purposes we treat milliseconds as minutes to simulate
         // actual bus times. Do not do this in your actual bus schedule program!
         Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
                                 this.RouteNumber,
                                 elapsedTime.Milliseconds,
                                 globalStartTime.ToShortTimeString());
     }  }

 class TestBus  {
     static void Main()
     {
         // The creation of this instance activates the static constructor.
         Bus bus1 = new Bus(71);

         // Create a second bus.
         Bus bus2 = new Bus(72);

         // Send bus1 on its way.
         bus1.Drive();

         // Wait for bus2 to warm up.
         System.Threading.Thread.Sleep(25);

         // Send bus2 on its way.
         bus2.Drive();

         // Keep the console window open in debug mode.
         System.Console.WriteLine("Press any key to exit.");
         System.Console.ReadKey();
     }  }  /* Sample output:
     Static constructor sets global start time to 3:57:08 PM.
     Bus #71 is created.
     Bus #72 is created.
     71 is starting its route 6.00 minutes after global start time 3:57 PM.
     72 is starting its route 31.00 minutes after global start time 3:57 PM.      
*/

1
在C++中,不存在静态构造函数这样的东西。

1

以下是C++17中最简单的静态构造函数:

#include <iostream>
class h {
    public:
    static inline int i;
    private:
    struct constructor {
        constructor() {
         i=3;
        }
    };
    static inline constructor cons;
};

int main() {
  std::cout << h::i;
}

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