11

反正在 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 行为和十分之一的派生类。

任何的想法?

4

5 回答 5

9

这是一种解决方案:

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 };
于 2008-08-29T20:11:52.930 回答
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 实例。

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

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

于 2008-08-29T16:14:57.600 回答
1

@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

于 2008-08-29T16:48:06.040 回答
1

我同意 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* 指针并处理强制转换等的“乐趣”。

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

于 2008-08-29T17:52:17.523 回答
0

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

于 2009-05-21T22:49:05.120 回答