【问题标题】:What is a static constructor?什么是静态构造函数?
【发布时间】:2011-08-13 19:42:48
【问题描述】:

在一次采访中问我这个问题:

什么是静态构造函数?

它存在于 C++ 中吗?如果有,请举例说明。

【问题讨论】:

标签: c++


【解决方案1】:

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

有关更多问题和兴趣,您可以阅读此主题:

What is the rationale for not having static constructor in C++?

【讨论】:

    【解决方案2】:

    在 C++ 中,没有静态构造函数。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          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;
          

          【讨论】:

          • 我猜class constructor 应该是class has_static_constructor 的朋友,才能做点有用的事?否则它只是另一个静态成员。
          • @davka 好话。事实上,嵌套并不是特别有用(除了限制范围,总是一件好事)。
          • @Nawaz 根据§7.1.5.3,它是。但是 GCC 把它扔给了我。
          • 您必须非常小心在这样一个非本地静态对象构造函数中放入什么。此构造函数何时运行是不确定的,它将处于启动过程的早期阶段。可以在 Scott Meyer 的 Effective C++(第 2 版中的第 47 项)中找到关于 NLSO 初始化的详尽解释。在构造函数运行时 RTOS 不可用的嵌入式世界中,这真的会咬你一口。
          • @Tod 非常有效的评论。不幸的是,延迟加载要复杂得多。本质上,我会用静态unique_ptr 解决它到一个嵌套类,该类包含所有“静态”成员实际上是非静态成员,并且一旦初始化为 0 和reset 到一个有效指针,一旦它是第一个访问。
          【解决方案6】:

          也许他们的意思是这样的:

          class Cat
          {
          private:
          Cat();
          public:
          static Cat getCat() {return Cat(); }
          }
          

          【讨论】:

            【解决方案7】:

            由于我们在 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
            

            我更喜欢这种方法;作为一线希望,它将非确定性初始化排除在外。

            不过有一个问题——如果您尝试初始化静态 const 变量,这种技术是不够的。对于静态 const 变量,您必须将它们设为类私有,并提供 getter 供外部人员读取。

            注意:我更新了这段代码——它编译并成功运行,没有警告通过:

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

            【讨论】:

            • 这是迄今为止关于如何/为什么/如果/何时使用一些技巧来模拟 C++ 中的静态构造函数的永恒问题的最佳答案。
            • @bearvarine 至于你最近关于 Meta-SO 的问题。是的,这是现在在 SO 无法生存的问题之一。我每天都会对这种形式的许多问题进行近距离投票,因为我认为它们对未来研究人员的价值很小(或者已经存在重复),并且 OP 在提出自己的问题之前一直缺少研究该网站。适度的语气已经改变,特别是对于这种口径的问题,因为同时答案已经无处不在。
            • 这会导致 VS2017 出现以下错误:严重性代码描述项目文件行抑制状态 LNK2001 未解析的外部符号“public: static class std::basic_string,类 std::allocator > Foo::my_text_str" (?my_text_str@Foo@@2V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@A )
            • 我刚刚遇到了这个问题。在您没有从头开始编写代码的实际项目中,您的解决方案并不总是可行的。
            【解决方案8】:

            模拟静态构造函数行为的其他方法是将实例变量与私有构造函数和静态工厂方法一起使用。

            Cat* Cat::give_birth() {
              static Cat *myone = NULL;
              if (myone == NULL) {
                myone = new Cat();
              }
              return myone;
            }
            

            【讨论】:

            • 应该使用 nullptr 设置/检查指针 myone(或者如果旧编译器至少为 NULL)而不是 0
            • 是的,从代码编写风格的角度来看,NULL 更好。
            【解决方案9】:

            静态构造函数用于初始化类的静态数据。 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
            

            【讨论】:

              【解决方案10】:

              请参阅my answer 以了解类似问题。 C# 的静态构造函数隐喻可以在 C++ 中完成。

              【讨论】:

                【解决方案11】:

                我认为静态构造函数用于初始化任何静态数据,或执行只需要执行一次的特定操作。在创建第一个实例或引用任何静态成员之前自动调用它。在 ++ 中,我们没有任何称为静态构造函数的东西,但您可以模仿静态构造函数的功能。看看这个 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.      
                */
                

                【讨论】:

                  【解决方案12】:

                  在 C++ 中,如果有人说“静态构造函数”,他们通常指的是“static initialization”(和破坏)。使用这个术语并不少见。

                  【讨论】:

                    【解决方案13】:

                    这是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;
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2011-05-29
                      • 2013-12-25
                      • 2011-10-23
                      • 1970-01-01
                      • 2011-03-01
                      • 2017-07-04
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-07-15
                      相关资源
                      最近更新 更多