2011-08-01 29 views
17

好的,標題很滿意,我認爲這可能是爲什麼它很難通過谷歌或本網站找到答案。這可能只是我不知道如何正確表達問題,但這裏有:將重載函數匹配到其多態參數

我在SimpleOpenGLRenderer類中有一系列方法,它們都帶有一個擴展Model類的參數。所以這個想法是,根據模型的類型,渲染器將調用知道如何渲染它的正確方法。這是建立在對問題的簡化可執行例如:

#include <stdio.h> 

class Model {}; 

class Cube : public Model {}; 

class Sphere : public Model {}; 

class Renderer 
{ 
    public: 
    virtual void renderModel(const Model& model) = 0; 
}; 

class SimpleOpenGLRenderer 
{ 
    public: 
    void renderModel(const Cube& model) 
    { 
     printf("Render the cube.\n"); 
    } 

    void renderModel(const Model& model) 
    { 
     printf("Throw an exception, my renderer does not support the model type you have provided.\n"); 
    } 

    void renderModel(const Sphere& model) 
    { 
     printf("Render the sphere.\n"); 
    } 
}; 

int 
main(int argc, char** argv) 
{ 
    Cube cube; 
    Model& model = cube; 
    SimpleOpenGLRenderer renderer; 

    renderer.renderModel(cube); 
    renderer.renderModel(model); 
} 

從示例的輸出是:

Render the cube. 
Throw an exception, my renderer does not support the model type you have provided. 

它可能看起來很明顯,以更豐富的C++開發者按照計劃,這並不工作,但它對我來說沒有意義。在運行時,我不會知道傳遞給渲染器的Model的確切類型(因此試圖通過重載來解決它)。從Java背景來看,我已經使用過此技術,並且在Java中,所調用的方法將與參數的類型運行時最匹配。在C++中,它似乎與引用的編譯時類型相匹配,即使該引用可能最終成爲某個子類 - 在我看來 - 更好地匹配另一個函數。

直到現在我已經採取這種運行時類型匹配爲理所當然。它在C++中是不是存在,還是我以錯誤的方式去做?我應該在C++中做不同的事情來實現它嗎?

謝謝,

Gary。

回答

17

C++中的重載在編譯時根據參數的靜態類型進行解析。

有被稱爲「雙分派」,可能是使用的技術:

class Model { 
    virtual void dispatchRender(Renderer &r) const = 0; 
}; 

class Cube : public Model { 
    virtual void dispatchRender(Renderer &r) const { 
     r.renderModel(*this); // type of "this" is const Cube* 
}; 

int main() { 
    Cube cube; 
    Model &model = cube; 
    SimpleOpenGLRenderer renderer; 
    cube.dispatchRender(renderer); 
} 

注意,Renderer基類需要包含SimpleOpenGLRenderer目前做的所有重載。如果你想要它特定於SimpleOpenGLRenderer什麼過載存在,那麼你可以在Model中放一個簡單特定的調度函數,或者你可以忽略這個技術,而是在SimpleOpenGLRenderer::renderModel中重複使用dynamic_cast來測試類型。

+2

謝謝。我以前沒有使用過這種模式。我猜這在Java中可能不常見。我做了一些測試,我想我可能會回到'dynamic_cast'解決方案。我希望有更優雅的東西,但雙派/訪客模式似乎引入了我不太喜歡的各種依賴和緊密耦合。 –

1

對於基於動態類型的「運行時過載」,可以使用visitor pattern

+0

啊,謝謝你的鏈接,我忘記了。所以,現在,只是偷了你的鏈接。呵呵。 :-) –

2

在您的代碼中,函數重載是基於參數的靜態類型解析的。

您可能需要的是double-dispatch機制,它非常接近Visitor模式。閱讀這些:

1

您的代碼運行時類型匹配的一個不錯的選擇,如果你使用它。在這裏,您將Cube收到Model&中,並簡單地將其傳遞給renderModel()。直到現在,您還沒有機會讓編譯器使用運行時類型。而是依賴於對象的靜態類型。

用2種方法可以使用運行時類型檢查。一種是使用dynamic_cast<>,另一種是在Model中提供接口方法。即

class Model { 
    virtual void print() { printf("throw..."); } // provide an interface method 
}; 

class Cube : public Model { 
    virtual void print() { print("Render cube\n"; } // implement it 
}; 

class Sphere : public Model { 
    virtual void print() { print("Render sphere\n"; } // implement it 
}; 

class SimpleOpenGLRenderer 
{ 
    public: 
    void renderModel(const Model& model) 
    { 
    model.print(); 
    } 
}; 
+0

考慮到靜態類型的分辨率,我想我會使用'dynamic_cast'解決方案,因爲缺乏更好的解決方案。我不希望'模型'負責渲染,因爲我已經有10個不同的'Renderer'已經以不同的方式執行渲染。 –

0

在C++中,調用重載的解析是在編譯時完成的。

要使有效實現取決於多態參數類型,您需要查閱該參數,即在參數上調用虛方法。

我認爲最簡潔的方法就是所謂的visitor pattern。您的SimpleOpenGLRenderer可以調用方法model.renderOn(*this)。然後Model::renderOn是一組重載,每種可能類型的呈現器都有一個重載,或者它是使用dynamic_cast來發現呈現器類型的單個虛擬方法。無論如何,它然後在渲染器上回調,但是現在該調用知道它是什麼類型的渲染器以及它本身是什麼類型,並且還可以調用非常特定的渲染方法,如SimpleOpenGLRenderer::renderCube

乾杯,

0

這裏的其他解決方案將做你想要的。但在我看來,這是以複雜性爲代價的。如果您的問題與所描述的完全相同,我會建議改爲更改解決方案的體系結構。渲染者是不是試圖做模型的工作?我所看到的是一種超載生成的切換語句。

如何使模型渲染自理,比如用一些類產品更原始的繪圖方法:

class Cube : public Model { 
    render(RenderTool& tool) { 
    tool.renderCube(); //or even more low level 
    } 
}; 

class SimpleOpenGLRenderer { 
    public: 
    RenderModel(Model& model) { 
    model.render(renderTool); 
    } 
    private: 
    SomeOpenGLRenderingTool renderTool; 
}; 
+0

我沒有看到這比其他方法簡單。如果我使用「工具」來渲染渲染,那麼渲染器是什麼?這感覺就像一個不需要的中間人。我不希望'Model'負責渲染。渲染可以通過多種不同的方式完成,甚至可以使用不同的圖形API。 「模型」的責任僅僅是描述幾何。 –

+0

我儘量遵守信息專家的原則。在這種情況下,模型是唯一知道形狀的人。打開類型來創建一個外部對象的繪圖是IMO不必要的封裝破壞。渲染器的用途是一個有效的問題。 IMO也許應該使用渲染器而不是反之。 – daramarak