【问题标题】:C++ design with static methods使用静态方法的 C++ 设计
【发布时间】:2011-02-19 16:16:20
【问题描述】:

我想用静态方法定义为 X 类:

class X
{
 static string get_type () {return "X";}
 //other virtual methods
}

我想强制从 X 继承的类重新定义 get_type() 方法 并返回不同于“X”的字符串(如果他们现在只是重新定义 get_type 我很高兴)。

我该怎么做?我知道我不能有虚拟静态方法。

编辑:问题不是关于 type_id,而是关于一个静态方法 应该被覆盖。例如

class X {
 static int getid() {return 1;}
}

【问题讨论】:

  • 你似乎想要静态继承。我以为只有我一个!你能不能把它变成一个虚拟成员函数,或者结合单例设计模式?
  • 如果没有使用示例,这个问题就毫无意义。如前所述,答案很简单:只需重新定义派生类中的方法并返回您想要的任何内容。这里不需要任何“虚拟”。简单的。如果这不是您需要的,您必须解释原因。
  • @user231536:请说明您是否需要编译时或运行时多态性。你能发布一点关于你将如何使用get_type的sn-p吗?

标签: c++ static


【解决方案1】:

不要那样做,改用typeid

【讨论】:

  • 我知道我可以使用 typeid。我想重新表述一下关于派生类覆盖的一般静态方法的问题。
【解决方案2】:

长话短说,你做不到。要求派生类覆盖基类函数的唯一方法是使其成为纯虚拟(不能是静态的)。

【讨论】:

    【解决方案3】:

    由于多种原因,您不能这样做。您不能在 X 中定义函数并使其成为纯虚拟函数。你根本不能有虚拟静态函数。

    为什么它们必须是静态的?

    【讨论】:

      【解决方案4】:
      template<int id>
      class X {
      public:
          static int getid() { return id; }
      };
      
      class Y : public X<2> {
      };
      

      您没有重写该方法,但您已强制每个子类提供一个 ID。警告:我没有尝试过,可能有一些微妙的原因导致它不起作用。

      【讨论】:

      • 那么您是否还必须确保每个孩子使用唯一的 ID?
      • @Mark B,一个类完全不可能对它一无所知的其他类实施约束。例如,它们可能位于完全不同的源文件中。你能做的最好的事情就是让程序员考虑一下。希望他们不会效仿这个例子:xkcd.com/221
      • 不错!但是 OP 希望 getid 返回字符串。我想getid 可以将键返回到字符串映射中。
      • 这不是禁止可能使用多态性吗?每个 Y 都派生自不同的类型,没有办法安全地转换它们吗?
      • @pmr,看起来可能是这样,但您始终可以在 X 之上创建另一个不依赖于模板参数的基类。
      【解决方案5】:

      我想说你知道原因,但以防万一这里有一个很好的解释:

      http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/cplr139.htm

      看来您将不得不设计自己的出路。也许是一个包装单例的虚函数?

      【讨论】:

        【解决方案6】:

        如果我没记错的话,要调用静态方法,您必须通过指定类的确切名称来调用该方法,例如X::get_type();DerivedClass::get_type() 等,无论如何,如果在对象上调用,不考虑对象的动态类型。因此,至少在特定情况下,它可能仅在您不期望多态行为时在模板化上下文中有用。

        但是,我不明白为什么不能强制每个有趣的类(继承与否,因为“编译时多态性”并不关心)为模板提供此功能。在以下情况下,您必须专门化 get_type 函数,否则您将遇到编译时错误:

        #include <string>
        
        struct X {}; 
        struct Derived: X {};
        
        template <class T> std::string get_type() {
            static_assert(sizeof(T) == 0, "get_type not specialized for given type");
            return std::string(); 
        }
        
        template <> std::string get_type<X>() {
            return "X"; 
        }
        
        int main() {
            get_type<X>();
            get_type<Derived>(); //error 
        }
        

        (static_assert 是 C++0x,否则使用你最喜欢的实现,例如BOOST_STATIC_ASSERT。如果你对特化函数感觉不好,请特化一个结构体。如果你想在有人不小心尝试时强制出错将其专门用于不是从 X 派生的类型,那么 type_traits 也应该可以。)

        【讨论】:

          【解决方案7】:

          给你

          class X
          {
            static string get_type() {return "X"; }
          };
          
          class Y : public X
          {
            static string get_type() {return "Y"; }
          };
          

          上面的代码完全符合您的要求:派生类重新定义get_type 并返回一个不同的字符串。如果这不是您想要的,您必须解释原因。您必须解释您正在尝试做什么以及您期望从该静态方法中获得什么行为。如果您的原始问题完全不清楚。

          【讨论】:

          • “我想强制继承自 X 的类重新定义 get_type() 方法并返回不同于 X 的字符串” ...他的要求是如何做到的?
          • @Michael Mrozek:“如何”是什么意思? 字面意思精确 - 就是这样!在我上面的示例中,Y 类重新定义了方法并返回了一个不同的字符串。完全按照要求。您认为问题在哪里?
          • “我想强制继承自 X 的类重新定义 get_type() 方法并从 X 返回字符串不同”。 string Y::get_type() {return "X";} 可以在您的代码中使用。所以会留下Y::get_type undefined
          • @Michael Mrozek:我不认为“强迫”是问题的关键部分。对我来说,这个问题通常看起来像是一些“静态多态性”。虽然不清楚需要什么样的多态性。
          【解决方案8】:

          您提到了一些关于保证子类型为您的函数产生唯一值的地方。正如其他人所说,这在编译时是不可能的[至少,不使用模板,这可能是也可能是不可接受的]。但是,如果您将其延迟到运行时,您可能会执行类似的操作。

          class Base {
            static std::vector<std::pair<const std::type_info*, int> > datas;
            typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
          public:
            virtual ~Base() { }
            int Data() const {
              const std::type_info& info = typeid(*this);
              for(iterator i = datas.begin(); i != datas.end(); ++i)
                if(*(i->first) == info) return i->second;
              throw "Unregistered Type";
            }
          
            static bool RegisterClass(const Base& p, int data) {
              const std::type_info& info = typeid(p);
              for(iterator i = datas.begin(); i != datas.end(); ++i) {
                if(i->second == data) {
              if(*(i->first) != info) throw "Duplicate Data";
              return true;
                }
                if(*(i->first) == info) throw "Reregistering";
              }
              datas.push_back(std::make_pair(&info, data));
              return true;
            }
          };
          std::vector<std::pair<const std::type_info*, int> > Base::datas;
          
          class Derived : public Base { };
          const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);
          
          class OtherDerived : public Base { };
          const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception
          

          警告:这是完全未经测试的。如果你这样做,在进入 main 之前会抛出异常。您可以将注册移到构造函数中,如果愿意,可以接受注册检查的每个实例开销。

          为了简单起见,我选择了一个无序向量;我不确定type_info::before 是否提供了必要的语义来用作地图的谓词,并且大概你不会有这么多派生类,以至于线性搜索无论如何都会有问题。我存储了一个指针,因为您不能直接复制 type_info 对象。这主要是安全的,因为typeid 返回的对象的生命周期是整个程序。程序关闭时可能会出现问题,我不确定。

          我没有尝试防止初始化错误的静态顺序。正如所写,这将在某些时候失败。

          最后,不,它不是静态的,但无论如何,“静态”和“虚拟”并没有真正的意义。如果您没有要操作的类型的实例,那么您如何知道选择哪个覆盖的方法?在某些情况下,您可能希望在没有实际对象的情况下合理地调用静态方法,但这并不常见。

          *edit:另外,我不确定它如何与动态链接库等交互。我怀疑 RTTI 在这些情况下不可靠,所以显然这同样不可靠。

          【讨论】:

            【解决方案9】:

            使用 Delphi,它支持类上的虚拟静态成员。 ;>

            【讨论】:

              【解决方案10】:

              很抱歉复活了这个帖子,但我也刚刚遇到了这个道德危机。这是一个非常大胆且可能很愚蠢的声明,但我完全不同意大多数人所说的 static virtual 没有任何意义。这种困境源于静态成员的常用方式与它们在下面实际执行的操作。

              人们经常使用静态类和/或成员来表达事实 - 如果实例是相关的,那么对于所有实例都是正确的,或者在静态类的情况下只是关于世界的事实。假设您正在为哲学课建模。您可以定义abstract class Theory 来表示要教授的理论,然后在TheoryOfSelfTheoryOfMind 等中从Theory 继承。要教授理论,您真的需要一种称为express() 的方法,它使用适合听众的特定词组来表达理论。有人会假设任何继承类都应该公开一个相同的方法express()。如果可以的话,我会使用static virtual Theory.express() 来模拟这种关系——它既是一个超越实例概念(因此是静态的)和非特定的事实陈述,需要每个type 的特定实现理论(因此是虚拟的)。

              然而,我完全同意人们以 static 实际所做的事情为由来证明禁令是正当的——这在编码原则方面非常合理,问题源于人们通常对现实世界建模的习惯方式。

              我能想到的解决此问题的最佳方法是将 Theory 建模为单例 - 可能有一个理论的实例,但只有一个他们。如果你想要一个替代方案,它是一个不同的类型,所以创建一个新的派生类。对我来说,这种方法似乎是武断的,并引入了不必要的噪音。

              【讨论】:

                猜你喜欢
                • 2013-09-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-10-06
                • 2011-01-17
                • 2014-01-17
                • 1970-01-01
                • 2011-01-03
                相关资源
                最近更新 更多