【问题标题】:C++ static const members overridingC++ 静态 const 成员覆盖
【发布时间】:2012-11-12 07:59:31
【问题描述】:

考虑以下内容。

struct A {
    static const int X = 1;
    static void printX() {std::cout << "X " << X << std::endl; };
};

struct B: public A {
    static const int X = 2;
};

int main(argc argv){
    B b;
    b.printX();
}

如何强制b.printX() 打印值 2?
常量和方法都必须是静态的。因此,虚方法不适合。

对于那些认为他们比我更了解我的任务并希望看到我重新思考它的人,我将解释我的努力目标:)
想象一下具有基于一组静态常量的行为的类。实现具有不同常量集并因此具有不同行为的子类的最简单方法是从具有特定常量值集的前一个类派生类。可以使用虚函数来解决该任务。当然可能,毫无疑问。但是根据模型化实体的理论,这种解决方案将不是很纯粹。在这种情况下,使用虚拟方法将比正确实现更多的是技巧。
例如,IR 通道具有不同的脉冲持续时间和封装结构时序。用一组特定的常量值定义一组子类(不同的 IR 通道实现)很方便。这些值是静态的,因为它们对于 class 和 const 的每个对象都是通用的,因为它们仅在编译时需要。而且由于基类和子类的内部实现略有不同,它们之间的最佳关系是super class - child class
现在是我原来的问题的理由吗?

【问题讨论】:

  • 不可能。你想要动态调度而不使用virtual,这是矛盾的(除非你手动重新实现你的对象系统)。
  • 来吧,要强制 b.printX() 打印 2 只需将 X 替换为 printX() 定义中的 2 ;-)
  • 不,我不想使用动态调度。我想永远使用静态成员。
  • 除了@BasileStarynkevitch 的评论,考虑一下:A 怎么知道B 的内容?如果你说“让 B 成为 A 的朋友”,那么这只是糟糕的设计,A 仍然必须将 X 称为 B::X 才能获得 B 对 X 的值。(事实上你可以引用 A: :X 因为 X 只是为了方便。)
  • @jpm,展览structs之间的友谊目的是什么?

标签: c++ static-members


【解决方案1】:

您将需要一个模板,并更改继承以使用该模板,正如您将看到的。诀窍是无论派生类是否有一个 X 来掩盖基类 X ,它都可以工作。

template<class C>
struct A {
    static const int X = 1;
    template<typename T>
    static int getX(decltype(T::X)*) 
        { return T::X; }
    template<typename T>
    static void getX(...)
        { return X; }
    static void printX()
        { std::cout << "X " << getX<C>(0) << std::endl; }
};

struct B: public A<B> {
    static const int X = 2;
};

struct B2: public A<B2> {
    // No X
};

int main(){
    B b;
    b.printX(); // Prints X 2
    B2 b2;
    b2.printX(); // Prints X 1
}

【讨论】:

  • 暖和!但是有点麻烦。顺便说一句,你为什么使用带星号的 decltype(T::X) ?为什么是指针,而不是值?
  • +1 不错的解决方案! (@OlegG 指针是必需的,因此虚拟参数 0 的工作原理与 X 的类型无关)
  • 不错的解决方案,但我仍然认为设计很糟糕,Oleg 需要坐下来重新考虑一下,或者告诉我们他实际上想要做什么。
  • @JiveDadson:一般来说,由于继承的必要性,在这个任务中使用模板并不是很好的解决方案。您使用 decltype() 的想法很好,但最好为当前类型值发明一些类似的东西。然后我们就写 cout
  • @OlegG 我第一次尝试它试图定义一个类型 D 以便您可以在基类中编写 D::X,但我拥有的编译器(VC++ 2012)拒绝了它。我认为我是对的,这是错误的,但我不喜欢发布无法编译的答案。我更改了代码,所以它使用 getX(0),它不如 D::X 漂亮,但它确实有效。
【解决方案2】:

只需将 X 的值作为模板参数:

#include <iostream>

template<int XValue=1>
struct A {
        static const int X = XValue;
        static void printX() {std::cout << "X " << X << std::endl; };
};

template<int XValue=2>
struct B: public A<XValue> {
};

struct C: public B<3> {
};

int main(int, char**){
        B<> b;
        b.printX();
}

【讨论】:

    【解决方案3】:

    根据定义,您对静态成员所做的任何事情都将是“遮蔽”,而不是“覆盖”。您可以在“B”中重新实现“printX()”,但不会真正覆盖该行为;因为这会使用遮蔽,所以行为将完全取决于编译时类型,而不是运行时类型。

    【讨论】:

    • 我认为 OP 可能会混淆 RE 的区别,因为允许在该类的实例上调用类的静态。
    【解决方案4】:

    我不会使用statictemplate,而是使用常规常量属性和构造函数。

    例如:

    #include <iostream>
    
    struct A {
        A(const char* fn, const int X) : filename(fn), X(X) {};
        void print() { std::cout << "X = " << X << ", FN = " << filename << std::endl; };
    
      protected:
        const char* filename;
        const int X;
    };
    
    struct B : public A {
        B() : A("data.dat", 5) {};
    };
    
    int main(int, char **) {
        B b;
        b.print();
    }
    

    从功能上讲,它完全符合您的要求。输出:

    X = 5, FN = data.dat
    

    ——现在编译器的工作就是优化这些常量。如果您不打算使用数千个对象B,则可能不值得担心使这些常量static

    【讨论】:

      【解决方案5】:

      简短的回答:你不能。

      略长、更复杂的答案:嗯,也许你可以。使用模板

      #include <iostream>
      
      template <typename T> struct A 
      {
          static const int X = 1;
      
          static void printX() 
          { 
              std::cout << "X=" << T::X << std::endl; 
          }
      };
      
      struct B : public A<B> 
      {
          static const int X = 2;
      };
      
      int main(int, char **)
      {
          B b;
      
          b.printX();
      
          return 0;
      }
      

      【讨论】:

      • 继承的下一步(C 来自 B)将杀死 printX()
      • 当然,但坦率地说,如果你需要在更深层次上做这件事,你的设计很糟糕,也许你应该重新考虑一下。
      【解决方案6】:

      好的,我会一起玩...你想嵌套不止一层。很好。

      #include <iostream>
      
      template <int XX> struct T
      {
          static const int X = XX;
      
          static void printX()
          {
              std::cout << "X=" << X << std::endl;
          }   
      };
      
      struct AA 
      {
          static const int X = 1;
      
          /* all other members go here */
      };
      
      struct A : public AA, public T<AA::X>
      {
          /* empty - put stuff in AA instead */
      };
      
      struct BB : public AA
      {
          static const int X = 2;
      };
      
      struct B : public BB, public T<BB::X>
      {
      };
      
      struct CC : public BB
      {
          static const int X = 3;
      };
      
      struct C : public CC, public T<CC::X>
      {
      };
      
      struct DD : public CC
      {
          static const int X = 4;
      };
      
      struct D : public DD, public T<DD::X>
      {
      };
      
      int main(int, char **)
      {
          A a;
          B b;
          C c;
          D d;
      
          a.printX();
          b.printX();
          c.printX();
          d.printX();
      
          return 0;
      }
      

      您甚至可以跳过每节课中的static const int X = ...;,而只需根据需要使用public T&lt;1&gt;public T&lt;2&gt; 等。

      【讨论】:

      • 当然,在这种设计中,您不能将 B 转换为 A,因为它们不是直接相互派生的。但是,嘿……你赢了一些,你输了一些。
      • 从 A 直接推导 B 是必要的。
      • 是现在吗?哎呀... 翻白眼
      • 对不起,我没有深入分析新的解决方案,因为它看起来很麻烦,而且不合适。一定有更简单的解决方案
      猜你喜欢
      • 2019-04-30
      • 2011-05-08
      • 2020-11-21
      • 2016-01-19
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 2012-02-02
      • 2013-04-13
      相关资源
      最近更新 更多