在C ++中模拟类的虚拟静态成员?

时间:2020-03-05 18:45:00  来源:igfitidea点击:

无论如何,在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行为和十分之一的派生类。

任何的想法?

解决方案

回答

似乎答案是在问题中,我们建议的方法似乎是正确的方向,除了如果我们有大量的共享成员,我们可能希望将它们收集到一个struct或者class中,然后将其作为基类的构造函数的参数。

如果坚持要将"共享"成员实现为派生类的静态成员,则可以自动生成派生类的代码。 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实例。

因此,现在我们每个描述符都有一个实例,并且在执行结束时也将其删除。

请注意,如果派生类的唯一目的是提供相关的"描述符"数据(即,与实现虚拟函数相反),则应使基类成为非抽象,并仅使用适当的实例创建一个实例。每次描述符。

回答

@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共享指针。

回答

我同意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 *指针并处理转换等。

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

回答

这是一种解决方案:

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

回答

听起来好像我们正试图避免在叶类上重复代码,所以为什么不从基类派生中间基类呢?这个中间类可以保存静态数据,并使所有叶类都从中间基类派生。前提是需要在所有派生类上保存一个静态数据,这在示例中似乎是这样。