关于c ++:强制调用基类虚函数向下长链


Force calling base class virtual function down long chains

我的问题用更长的继承链扩展了这个问题

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
////////// View ///////////////
class View{
public:
    void Render(){
        std::cout <<"View::Render" << std::endl;
        render();
    }
protected:
    virtual void render() = 0;
};
////////// ImageView ///////////////
class ImageView : public View{
public:
protected:
    void render(){
        std::cout <<"ImageView:render" << std::endl;
    }
};
////////// Sprite ///////////////
class Sprite : public ImageView{
public:
protected:
    void render(){
        std::cout <<"Sprite:render" << std::endl;
    }
};
////////// Utility ///////////////
void Draw(View *vw){
    vw->Render();
}
////////// main ///////////////
int main(){
    std::cout <<"... Draw ImageView ..." << std::endl;
    ImageView *imgvw = new ImageView;
    Draw(imgvw);
    delete imgvw;

    std::cout <<"... Draw Sprite ..." << std::endl;
    Sprite *sp = new Sprite;
    Draw(sp);
    delete sp;

    return 0;
}

实际产量:

1
2
3
4
5
6
.. Draw ImageView ...
View::Render
ImageView:render
... Draw Sprite ...
View::Render
Sprite:render

所需输出:

1
2
3
4
5
6
7
.. Draw ImageView ...
View::Render
ImageView:render
... Draw Sprite ...
View::Render
ImageView:render
Sprite:render

我试图只保留一个应该调用所有虚拟方法链的基类公共方法。C++中有可能这样吗?


根据这个问题(如果我重写一个基类的虚函数,我可以调用它吗?),更改定义如下:

1
2
3
4
5
6
7
8
class Sprite : public ImageView{
public:
protected:
    void render(){
        ImageView::render(); // this calls the superclass' virtual method
        std::cout <<"Sprite:render" << std::endl;
    }
};


我已经使用嵌套类构造函数完成并实现了您想要的。它相当难看,有相当多的样板,但我相信它完全符合你的要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <string>
#include <iostream>

using namespace std;

class View
{
protected:
    class ViewRender
    {
    public:
        ViewRender(const View &v)
        {
            cout <<"ViewRender:constructor" << endl;
        }
    };

    // returns a reference to a temporary.  I'm not sure how to avoid doing this
    // the reference isn't actually used, and we can't pass it a reference to one
    // and have it populate it since the trick is in the constructor.

    virtual ViewRender &MakeRender() = 0;
public:
    void Render() { MakeRender(); }
};

class ImageView : public View
{
protected:
    class ImageViewRender : public View::ViewRender
    {
    public:
        ImageViewRender(const View &v) : ViewRender(v)
        {
            cout <<"ImageViewRender:constructor" << endl;
        }
    };

    virtual ImageViewRender &MakeRender() { return ImageViewRender(*this); }
};

class Sprite : public ImageView
{
protected:
    class SpriteRender : public ImageView::ImageViewRender
    {
    public:
        SpriteRender(const View &v) : ImageViewRender(v)
        {
            cout <<"SpriteRender:constructor" << endl;
        }
    };

    virtual SpriteRender &MakeRender() { return SpriteRender(*this); }
};

class AwesomeSprite : public Sprite
{
protected:
    class AwesomeSpriteRender : public Sprite::SpriteRender
    {
    public:
        AwesomeSpriteRender(const View &v) : SpriteRender(v)
        {
            cout <<"AwesomeSpriteRender:constructor" << endl;
        }
    };

    virtual AwesomeSpriteRender &MakeRender() { return AwesomeSpriteRender(*this); }
};

int main()
{
    AwesomeSprite as;
    ImageView &iv = as;

    cout <<"rendering AwesomeSprite..." << endl;
    as.Render();

    cout <<"rendering Awesome (downcast to ImageView)..." << endl;
    iv.Render();

    system("pause");
    return 0;
}

严格地说,我仍然认为没有一种方法可以强迫人们正确地进行渲染,但是使用这种方法,调用任何渲染器都会自动调用它下面的所有渲染器(这是不可能的)。一个聪明的子类仍然可以自由地编写自己的呈现子类,而不必从上面的呈现类派生它,但是这样他就不能免费获得所有的呈现行为,所以我认为这尽可能接近你想要的。

渲染完全是在---Render嵌套类的构造函数中完成的,利用了这样一个事实,即始终为构造函数执行链接。用户只需提供受保护的虚拟函数makerender,该函数返回所需类型的渲染器引用。用户只是或多或少地声明MakeRender函数,这一事实迫使他们生成类的一个实例,并阻止他们在构造函数之外执行渲染工作(这会破坏目的)。


装饰图案可以在这里有所帮助,但这又不是你想要的。


如果你想让ImageView::render()的动作不管派生版本怎么说,为什么不创建一个私有的、非虚拟的ImageView::base_render()并在派生render()之前或之后从ImageView::render()调用它呢?