【问题标题】:Are static variables in a base class shared by all derived classes?基类中的静态变量是否由所有派生类共享?
【发布时间】:2010-11-26 07:54:24
【问题描述】:

如果我有类似的东西

class Base {
    static int staticVar;
}

class DerivedA : public Base {}
class DerivedB : public Base {}

DerivedADerivedB 会共享同一个 staticVar,还是各自拥有自己的?

如果我想让他们每个人都有自己的,你会建议我做什么?

【问题讨论】:

    标签: c++ inheritance static


    【解决方案1】:

    它们将共享相同的staticVar 实例。

    为了让每个派生类都有自己的静态变量,您需要声明另一个具有不同名称的静态变量。

    然后,您可以在基类中使用一对虚拟函数来获取和设置变量的值,并在每个派生类中覆盖该对,以获取和设置该类的“本地”静态变量。或者,您可以使用返回引用的单个函数:

    class Base {
        static int staticVarInst;
    public:
        virtual int &staticVar() { return staticVarInst; }
    }
    class Derived: public Base {
        static int derivedStaticVarInst;
    public:
        virtual int &staticVar() { return derivedStaticVarInst; }
    }
    

    然后您将其用作:

    staticVar() = 5;
    cout << staticVar();
    

    【讨论】:

    • (请注意,CRTP 还允许派生类获取某些静态变量的自己的版本。)
    • 我想我会使用虚函数,但为了澄清一下,如果我在派生类中声明一个同名的静态变量,它基本上会覆盖基类,对吧?所以我可以指望它存在于所有派生类中......
    • 实际上可以在派生类中声明一个同名的静态变量。如果你这样做,我会用它的类名来限定对该变量的每个引用,即:virtual int &amp;staticVar() { return Derived::staticVarInst; }
    【解决方案2】:

    为确保每个类都有自己的静态变量,您应该使用"Curiously recurring template pattern" (CRTP)

    template <typename T>
    class Base
    {
        static int staticVar;
    };
    
    template <typename T> int Base<T>::staticVar(0);
    
    class DerivedA : public Base<DerivedA> {};
    class DerivedB : public Base<DerivedB> {};
    

    【讨论】:

    • CRTP 并没有真正参与其中,更多的是基类的专业化;可以使用 Base 和 Base 之类的任何东西。但是对于那些不知道 CRTP 的人来说,插入讨论是件好事;)
    • 这确实是 CRTP(见en.wikipedia.org/wiki/Curiously_recurring_template_pattern);从模板化的基类继承,并以您自己的类名作为参数是 CRTP 的定义。
    • 我知道我在讨论中非常迟到了,但它可能会帮助像我一样学习的谷歌同事。有人能指出这个答案和公认答案之间的优缺点吗?
    • 接受的答案要求您声明一个新的静态变量和访问方法。这个答案更优雅:您只编写一次并获得尽可能多的静态变量:每个模板专业化一个。
    【解决方案3】:

    他们将共享同一个实例。

    您需要为每个子类声明单独的静态变量,或者您可以考虑一个简单的静态映射,您可以在其中存储派生类引用的变量。


    编辑:可能的解决方案是将您的基类定义为模板。在这个模板中定义一个静态变量意味着每个派生类都有它自己的静态实例。

    【讨论】:

    • 没有名为Derived 的模板。相反,在这种情况下,它是template&lt;typename&gt; struct Base { static int staticVar; protected: ~Base() { } }; template&lt;typename D&gt; int Base&lt;D&gt;::staticVar; struct DerivedA : Base&lt;DerivedA&gt; { }; struct DerivedB : Base&lt;DerivedB&gt; { };。现在有两个静态变量,您可以使用DerivedB::staticVarDerivedA::staticVar 访问它们
    【解决方案4】:

    在你的情况下只有一个staticVarBase::staticVar

    当你在一个类中声明一个静态变量时,这个变量是为那个类单独声明的。在您的情况下,DerivedA 甚至看不到 staticVar(因为它是私有的,不受保护或公共的),所以它甚至不知道存在 staticVar 变量。

    【讨论】:

      【解决方案5】:

      我知道这个问题已经得到解答,但我想提供一个静态成员继承的小例子。这是一种很好的方式来展示静态变量和相应的构造函数的有用性以及正在发生的事情。

      FooBase.h

      #ifndef FOO_BASE_H
      #define FOO_BASE_H
      
      #include <string>
      
      class FooBase {
      protected:
          std::string _nameAndId;
      private:
          std::string _id;
          static int _baseCounter;
      
      public:
          std::string idOfBase();
          virtual std::string idOf() const = 0;
      
      protected:
          FooBase();    
      };
      
      #endif // !FOO_BASE_H
      

      FooBase.cpp

      #include "FooBase.h"
      #include <iostream>
      
      int FooBase::_baseCounter = 0;
      
      FooBase::FooBase() {
          _id = std::string( __FUNCTION__ ) + std::to_string( ++_baseCounter );
          std::cout << _id << std::endl;
      }
      
      std::string FooBase::idOfBase() {
          return _id;
      }
      
      std::string FooBase::idOf() const {
          return "";
      } // empty
      

      DerivedFoos.h

      #ifndef DERIVED_FOOS_H
      #define DERIVED_FOOS_H
      
      #include "FooBase.h"
      
      class DerivedA : public FooBase {
      private:    
          static int _derivedCounter;
      
      public:
          DerivedA();
      
          std::string idOf() const override;
      };
      
      class DerivedB : public FooBase {
      private:
          static int _derivedCounter;
      
      public:
          DerivedB();
      
          std::string idOf() const override;
      };
      
      #endif // !DERIVED_FOOS_H
      

      DerivedFoos.cpp

      #include "DerivedFoos.h"
      #include <iostream>
      
      int DerivedA::_derivedCounter = 0;
      int DerivedB::_derivedCounter = 0;
      
      DerivedA::DerivedA() : FooBase() {
          _nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedA::_derivedCounter );
          std::cout << _nameAndId << std::endl;
      }
      
      std::string DerivedA::idOf() const {
          return _nameAndId;
      }    
      
      DerivedB::DerivedB() : FooBase() {
          _nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedB::_derivedCounter );
          std::cout << _nameAndId << std::endl;
      }
      
      std::string DerivedB::idOf() const {
          return _nameAndId;
      }
      

      ma​​in.cpp

      #include "DerivedFoos.h"
      
      int main() {
          DerivedA a1;  
          DerivedA a2;
          DerivedB b1;
          DerivedB b2;
      
          system( "PAUSE" );
          return 0;
      }
      

      如果__FUNCTION__ 在您的构造函数中不适合您,那么您可以使用类似的东西来替换它,例如__PRETTY_FUNCTION____func__,或者手动输入每个类的名称:(

      【讨论】:

        【解决方案6】:

        @einpoklum 给出的示例代码无法正常工作,因为缺少静态成员 foo_ 的初始化、FooHolder 声明中缺少继承以及缺少 public 关键字,因为我们正在处理类。这是它的固定版本。

        #include <iostream>
        #include <string>
        
        class A {
        public:
            virtual const int& Foo() const = 0;
        };
        
        template <typename T>
        class FooHolder : public virtual A {
        public:
            const int& Foo() const override { return foo_; }
            static int foo_;
        };
        
        class B : public virtual A, public FooHolder<B> { };
        class C : public virtual A, public FooHolder<C> { };
        
        template<>
        int FooHolder<B>::foo_(0);
        template<>
        int FooHolder<C>::foo_(0);
        
        int main()
        {
          B b;
          C c;
          std::cout << b.Foo() << std::endl;
          std::cout << c.Foo() << std::endl;
        }
        

        【讨论】:

          【解决方案7】:

          唉,C++ 没有虚拟静态数据成员。有几种方法可以或多或少地模拟这一点:

          • @GregHewgill's solution 是否在每个派生类中复制了静态变量;这个解决方案简单明了,并且不引入额外的类,但我不喜欢这个,因为它很冗长,而且你必须相当自律。
          • @MarkIngram 建议使用CRTP-based solution,这样可以节省您大部分的打字时间;但是,它弄乱了继承结构,因为以前 A 的子类不再作为类真正相关。毕竟,两个具有相同名称但不同模板参数的模板类型可以是任意两种类型。

          我建议使用不同的基于CRTP 的解决方案,使用mix-in 类:

           class A {
                virtual const int& Foo() const = 0;
           }
          
           template <typename T>
           class FooHolder {
                static int foo_;
                const int& Foo() const override { return foo_; }
           }
          
           class B : A, virtual FooHolder<B> { }
          
           class C : B, virtual FooHolder<B> { }
          

          在子类中你唯一需要做的就是指出混合继承。我可能在这里遗漏了一些虚拟继承注意事项(因为我很少使用它)。

          请注意,您要么必须在某处实例化和初始化每个子类的静态变量,要么可以将其设为 inline 变量 (C++17) 并在模板中对其进行初始化。

          这个答案改编自我对dupe question 的回答。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-02-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多