【问题标题】:CRTP / Macros / Avoid casting pointer of derived classCRTP / 宏 / 避免转换派生类的指针
【发布时间】:2015-03-19 13:28:13
【问题描述】:

最近我一直在从事一些项目,但问题是我们遇到了需要能够做这样的事情的情况。

#define TYPED(Type) \
    virtual Type *typedThis() const { return (Type*) this; }

class A {
public: 
TYPED(A)
virtual void describe() { std::cout << "I am type A\n"; }
static int x;
};

int A::x = 1;

class B : public A {
public: 
TYPED(B) 
virtual void describe() { std::cout << "I am type B\n"; }
static int x;
};

int B::x = 2;

int
main(int argc, char** argv)
{
    B* b = new B();
    A* b2 = b;

    b->describe();
    b2->describe();

    std::cout << b->typedThis()->x << std::endl;
    std::cout << b2->typedThis()->x << std::endl; // AQUI DEBERIA DAR 2 !! :c
}

这当然只是一个玩具示例。我们想要做的基本想法是定义一个函数 typedThis(),它将指针转换为正确的类型,然后访问正确的变量 x,并打印出 2 而不是 1。

但是,输出如下:

I am type B
I am type B
2
1 //Here should also be 2

我发现真正有趣的是虚方法 describe() 似乎按照我们想要的方式工作。因此,我可以推断 typedThis() 方法也可以按照我们希望的方式工作。但如果是这样,为什么 C++ 将此指针视为 A* 而不是 B*。如果 C++ 将此指针视为 B*,那么它将使用正确的变量 x。 谁能给我解释一下?

我尝试使用 CRTP,但我不觉得这会使事情变得更容易,因为在项目中我们将使用很多(很多)不同的类,它们不断地在它们之间派生,我看到了一些关于如何在具有多重继承时使用 CRTP,但是它们确实很混乱,很难与我们目前所拥有的集成。

【问题讨论】:

  • 如果不是虚拟的,为什么应该是2?它将使用指针基类解析,而不是检查任何虚拟表来解析该函数​​。
  • pba 需要是虚拟的。
  • 是的。因为这只是一个玩具示例,所以我使用了一个虚拟方法,它只打印出一个数字。但是在实际代码中,我们希望访问一些类之间具有相同名称的属性。因此,假设 A 和 B 类都有一个 int 静态属性 x。然后我们收到一个指针 A*,但带有一个 B 实例的指针。然后如果我们想获得 x 的值。我们得到的是 A 类的 x 值,而不是 B 类的 x 值。在这个例子中,这相当于说,我们使用 A 的方法 pba() 打印出来,而不是方法 pba () 的 B.
  • x 的访问仅使用表达式的类型,而不是实际值,因为它是类的属性而不是对象之一。因为b-&gt;typedThis()B*,所以你得到B::x。然而,b2-&gt;typedThis() 是一个A*,所以你得到A::xb2 指向 B 的事实无关紧要。

标签: c++ pointers multiple-inheritance c-preprocessor crtp


【解决方案1】:

我消除了示例中的所有干扰:

class A {
public: 
    virtual A *typedThis() const { return (A*) this; }
    static int x = 1;
};

class B : public A {
public: 
    virtual B *typedThis() const { return (B*) this; }
    static int x = 2;
};

int main()
{
    B* b1 = new B;
    A* b2 = b1;

    std::cout << b1->typedThis()->x << "\n";
    std::cout << b2->typedThis()->x << "\n";
}

typedThis 不做任何事情

b1-&gt;typedThis() 返回指向 BB*

同样,b1 本身是一个 B*,它指向一个 B

b2-&gt;typedThis() 返回一个指向BA*

同样,b2 本身是一个 A*,它指向一个 B

所以b1-&gt;typedThis()b1相同,b2-&gt;typedThis()b2相同,示例的最后两行等价于:

    std::cout << b1->x << "\n";
    std::cout << b2->x << "\n";

还请注意,您的 C 样式转换会丢弃对象的 const 限定符。

【讨论】:

  • 它并没有完全解决我的问题,但却是最有帮助的答案,谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
  • 2011-02-01
  • 2016-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多