【问题标题】:Simulating a virtual static member of a class in c++?在 C++ 中模拟类的虚拟静态成员?
【发布时间】:2010-09-07 06:19:48
【问题描述】:

C++ 中有没有一种虚拟静态成员?

例如:

class BaseClass {
    public:
        BaseClass(const string& name) : _name(name) {}
        string GetName() const { return _name; }
        virtual void UseClass() = 0;
    private:
        const string _name;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass("DerivedClass") {}
        virtual void UseClass() { /* do something */ }
};

我知道这个例子很简单,但是如果我有一个复杂数据的向量,它对于所有派生类总是相同的,但需要从基类方法访问?

class BaseClass {
    public:
        BaseClass() {}
        virtual string GetName() const = 0;
        virtual void UseClass() = 0;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        virtual string GetName() const { return _name; }
        virtual void UseClass() { /* do something */ }
    private:
        static const string _name;
};

string DerivedClass::_name = "DerivedClass";

这个解决方案不能满足我,因为我需要在每个类中重新实现成员 _name 及其访问器 GetName()。就我而言,我有几个成员遵循 _name 行为和十分之一的派生类。

有什么想法吗?

【问题讨论】:

    标签: c++ virtual-functions


    【解决方案1】:

    这是一种解决方案:

    struct BaseData
    {
      const string my_word;
      const int my_number;
    };
    
    class Base
    {
    public:
        Base(const BaseData* apBaseData)
        {
            mpBaseData = apBaseData;
        }
        const string getMyWord()
        {
            return mpBaseData->my_word;
        }
        int getMyNumber()
        {
            return mpBaseData->my_number;
        }
    private:
        const BaseData* mpBaseData;
    };
    
    class Derived : public Base
    {
    public:
        Derived() : Base(&sBaseData)
        {
        }
    private:
        static BaseData sBaseData;
    }
    
    BaseData Derived::BaseData = { "Foo", 42 };
    

    【讨论】:

      【解决方案2】:

      似乎答案就在问题中-您建议的方法似乎是正确的方向,但如果您有大量共享成员,则可能希望将它们收集到结构或类中,并且过去作为基类构造函数的参数。

      如果您坚持将“共享”成员实现为派生类的静态成员,您也许可以自动生成派生类的代码。 XSLT 是自动生成简单类的绝佳工具。

      一般来说,该示例不需要“虚拟静态”成员,因为对于这些目的,您实际上不需要继承 - 相反,您应该使用基类并让它接受适当的值构造函数 - 可能为每个“子类型”创建参数的单个实例并将指针传递给它以避免共享数据的重复。另一种类似的方法是使用模板并将提供所有相关值的类作为模板参数传递(这通常称为“策略”模式)。

      总结 - 就原始示例而言,不需要这种“虚拟静态”成员。如果您仍然认为您正在编写的代码需要它们,请尝试详细说明并添加更多上下文。

      我上面描述的例子:

      class BaseClass {
          public:
              BaseClass(const Descriptor& desc) : _desc(desc) {}
              string GetName() const { return _desc.name; }
              int GetId() const { return _desc.Id; }
              X GetX() connst { return _desc.X; }
              virtual void UseClass() = 0;
          private:
              const Descriptor _desc;
      };
      
      
      class DerivedClass : public BaseClass {
          public:
              DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {}
              virtual void UseClass() { /* do something */ }
      };
      
      class DerDerClass : public BaseClass {
          public:
              DerivedClass() : BaseClass("Wowzer", 843,...) {}
              virtual void UseClass() { /* do something */ }
      };
      

      我想详细说明一下这个解决方案,也许会给出一个解决反初始化问题的解决方案:

      只需稍作改动,您就可以实现上述设计,而不必为派生类的每个实例创建“描述符”的新实例。

      您可以创建一个单例对象 DescriptorMap,它将保存每个描述符的单个实例,并在构造派生对象时使用它,如下所示:

      enum InstanceType {
          Yellow,
          Big,
          BananaHammoc
      }
      
      class DescriptorsMap{
          public:
              static Descriptor* GetDescriptor(InstanceType type) {
                  if ( _instance.Get() == null) {
                      _instance.reset(new DescriptorsMap());
                  }
                  return _instance.Get()-> _descriptors[type];
              }
          private:
              DescriptorsMap() {
                  descriptors[Yellow] = new Descriptor("Yellow", 42, ...);
                  descriptors[Big] = new Descriptor("InJapan", 17, ...)
                  ...
              }
      
              ~DescriptorsMap() {
                  /*Delete all the descriptors from the map*/
              }
      
              static autoptr<DescriptorsMap> _instance;
              map<InstanceType, Descriptor*> _descriptors;
      }
      

      现在我们可以这样做了:

      class DerivedClass : public BaseClass {
          public:
              DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {}
              virtual void UseClass() { /* do something */ }
      };
      
      class DerDerClass : public BaseClass {
          public:
              DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {}
              virtual void UseClass() { /* do something */ }
      };
      

      在执行结束时,当 C 运行时执行取消初始化时,它还会调用静态对象的析构函数,包括我们的 autoptr,它会删除我们的 DescriptorsMap 实例。

      所以现在我们有每个描述符的一个实例,它也在执行结束时被删除。

      请注意,如果派生类的唯一目的是提供相关的“描述符”数据(即与实现虚函数相反),那么您应该将基类设置为非抽象类,并创建一个实例每次使用适当的描述符。

      【讨论】:

        【解决方案3】:

        我同意 Hershi 的建议,即使用模板作为“基类”。根据您的描述,这听起来更像是使用模板而不是子类化。

        您可以按如下方式创建模板(尚未尝试编译):

        template <typename T> class Object { public: Object( const T& newObject ) : yourObject(newObject) {} ; T GetObject() const { return yourObject } ; void SetObject( const T& newObject ) { yourObject = newObject } ; protected: const T yourObject ; } ; class SomeClassOne { public: SomeClassOne( const std::vector& someData ) { yourData.SetObject( someData ) ; } private: Object<std::vector<int>> yourData ; } ;

        这将允许您使用模板类方法根据需要在自定义类中修改数据,这些自定义类使用数据并共享模板类的各个方面。

        如果您打算使用继承,那么您可能不得不求助于在 BaseClass 中使用 void* 指针并处理强制转换等“乐趣”。

        但是,根据您的解释,您似乎需要模板而不是继承。

        【讨论】:

          【解决方案4】:

          @Hershi:这种方法的问题是每个派生类的每个实例都有一个数据副本,这在某种程度上可能很昂贵。

          也许你可以尝试这样的事情(我在没有编译示例的情况下吐口水,但想法应该很清楚)。

          
          #include <iostream>
          #include <string>
          using namespace std;
          
          struct DerivedData
          {
            DerivedData(const string & word, const int number) :
              my_word(word), my_number(number) {}
            const string my_word;
            const int my_number;
          };
          
          class Base {
          public:
            Base() : m_data(0) {}
            string getWord() const { return m_data->my_word; }
            int getNumber() const { return m_data->my_number; }
          protected:
            DerivedData * m_data;
          };
          
          
          class Derived : public Base {
          public:
            Derived() : Base() {
              if(Derived::s_data == 0) {
                Derived::s_data = new DerivedData("abc", 1);
              }
              m_data = s_data;
            }
          private:
            static DerivedData * s_data;
          };
          
          
          DerivedData * Derived::s_data = 0; 
          
          int main()
          {
            Base * p_b = new Derived();
            cout getWord() << endl;
          }
          
          

          关于删除静态对象的后续问题:想到的唯一解决方案是使用智能指针,例如Boost shared pointer

          【讨论】:

            【解决方案5】:

            听起来好像您在尝试避免在叶类中复制代码,所以为什么不从基类派生一个中间基类。这个中间类可以保存静态数据,并让所有叶类都派生自中间基类。这假定需要在所有派生类中保存一个静态数据,从您的示例中似乎如此。

            【讨论】:

              猜你喜欢
              • 2010-12-21
              • 1970-01-01
              • 2023-03-23
              • 1970-01-01
              • 1970-01-01
              • 2013-07-27
              • 1970-01-01
              • 2010-11-17
              相关资源
              最近更新 更多