背景
我只是想探索出位可用的圖案和巧妙的使用的可能性(在C++)的結合。有很多關於如何使用多個虛擬繼承或pimpl習語的信息,並且關於橋接或嵌套泛化有很多不錯的主題,但是我無法爲此找到一個好主題。PIMPL方法具有多個虛擬繼承
我所知道的一樣(在某種程度上)類似的問題:
,但我認爲這不是一個重複的,值得它自己的答案。我的方法可能是絕對愚蠢的,所以請提醒我一些有用的方向。這不是一項家庭作業,因此對答案/語言/範例沒有任何限制,但是如果沒有關於個人偏好的討論,請嘗試對某個特定模式/解決方案有效的原因進行推理,原因可能是其他解決方案錯誤/容易出錯/ ...。
問題
什麼是執行以下結構的好辦法(也許組成,而不是繼承?)。如果重用代碼,更容易讀/寫,更容易擴展是主要關注點(也許您可以想到其他問題?),那麼有可能不同的答案是有效的:
我在當前實現中看到的主要問題這是不可擴展的,因爲我們每每得到一個類的組合我們要建模的每個屬性的值。爲了簡單起見,我使用具有屬性Projection(Ortographic或Perspective)和Behavior(FirstPerson或FreeLook)的Camera,但對於具有屬性P1,P2,P3,P4的任何實體A,這個想法可以是相同的每個可以是一個出許多亞型的P1 => P1.1,P1.2,... P2 => P2.1,P2.2,...
投影應確定
virtual const glm::mat4& getProjectionMatrix() = 0;
實施
而行爲應定義
virtual const glm::vec3& getForwardVector() = 0;
可能的解決方案1的實現:康貝ne pimpl成語與多個虛擬繼承
旁註:我使用名稱_this作爲pimpl指針,因爲_this->變量導致從我的角度來看非常可讀的代碼。當然,如果存在具有相同名稱的成員(不太可能但實際上可能),則存在將此混淆的危險。
Camera.h
class Camera{
protected:
/*Pimpl Idiom extended by multiple virtual inheritance for the AbstractImpl subclasses*/
class AbstractImpl;
std::unique_ptr<AbstractImpl> _this;
/*Private subclasses for AbstractImpl*/
class Ortographic;
class Perspective;
class FirstPerson;
class FreeLook;
class OrtographicFirstPerson;
class OrtographicFreeLook;
class PerspectiveFirstPerson;
class PerspectiveFreeLook;
};
AbstractImpl.h
class Camera::AbstractImpl
{
public:
/*Also contains all private data members of class Camera (pimpl idiom)*/
virtual const glm::vec3& getForwardVector() = 0;
virtual const glm::mat4& getProjectionMatrix() = 0;
};
class Camera::Ortographic:virtual Camera::AbstractImpl{public:virtual const glm::mat4& getProjectionMatrix() override;};
class Camera::Perspective:virtual Camera::AbstractImpl{public:virtual const glm::mat4& getProjectionMatrix() override;};
class Camera::FirstPerson:virtual Camera::AbstractImpl{public:virtual const glm::vec3& getForwardVector() override;};
class Camera::FreeLook:virtual Camera::AbstractImpl{public:virtual const glm::vec3& getForwardVector() override;};
class Camera::OrtographicFirstPerson:virtual Camera::Ortographic,virtual Camera::FirstPerson{};
class Camera::OrtographicFreeLook:virtual Camera::Ortographic, virtual Camera::FreeLook{};
class Camera::PerspectiveFirstPerson:virtual Camera::Perspective, virtual Camera::FirstPerson{};
class Camera::PerspectiveFreeLook:virtual Camera::Perspective, virtual Camera::FreeLook{};
這顯然不是每個屬性,但它看起來像第一眼看上去很多屬性或值的情況下,非常好用的該代碼可以很好地重用,因爲這兩種虛擬方法都是每次必需實施一次。此外,我認爲AbstractImpl的子類的部分代碼非常好讀,因爲它感覺像分配屬性。 「我希望這個班有這個屬性」。此外,所有子類都可以完全訪問AbstractImpl的數據成員和函數,這可能是必要的(假設被覆蓋的虛擬方法的返回值取決於私有數據成員的值)。
此外,這看起來像一個實現,可以由代碼生成器很好地支持,因爲您只需要對新子類進行正確的繼承。
可能的解決方案2:合成
我想到的另一個解決方案就是使用合成。
Camera.h
class Camera{
protected:
ProjectionType _projectionType;
CameraBehaviour _cameraBehaviour;
private:
class Impl;
std::unique_ptr<Impl> _this;
};
如果ProjectionType或CameraBehaviour sublcasses取決於相機的任何值::默認地將Impl的實例,有必要通過,我認爲這會弄亂代碼很多。在贊成方面,我們最終以較少的課程,因爲我們需要每個投影類型一個類和每個相機一個類。如果我們有許多可能的類型和行爲值,這可能是解決方案。
可能的解決方案3:繼承
當然,我們可以使用Camera的子類。在這種情況下,根本不可能使用pimpl,因爲所有需要受保護可見性的數據成員都不應該成爲pimpl的一部分。
Camera.h
class AbstractCamera{
protected:
/*All protected data members*/
private:
/*Maybe pimpl idiom makes no sense here because most of the variables might be protected*/
class Impl;
std::unique_ptr<Impl> _this;
};
PerspectiveFreeLookCamera.h
class PerspectiveFreeLookCamera: virtual AbstractCamera{
/*Override both methods*/
};
PerspectiveFirstPersonCamera.h
class PerspectiveFirstPersonCamera: virtual AbstractCamera{
/*Override both methods*/
};
和Ortographic類相同。在這裏,虛擬方法將實施多次爲所有透視/ Ortographic ...類共享相同的實現的方法
virtual const glm::mat4& getProjectionMatrix() = 0;
雖然所有FreeLook /第一人稱類分擔
virtual const glm::vec3& getForwardVector() = 0;
相同的實現感謝您花時間,我希望這可以被認爲是一個很好的問題,因爲我已經對它進行了相當多的考慮,因此它對於希望很多人很有意思:)
編輯:如果有人可以想象一個更好的標題FO這個問題,請隨時編輯,我很難找到一個好的標題。
可能的解決方案4:模板(由4.11 19:00 GMT + 2)
基於我曾經在模板的解決方案工作Yakk的答案。
相機。^ h
class Camera
{
public:
/*Forward declaration of public classes of camera
of course it would be possible to use their own header files to have a stricter one class
per file nature but i do not really see the need for this*/
template<typename t_projection, typename t_behaviour>
class Impl;
class AbstractImpl;
Camera(AbstractImpl *impl);
~Camera();
/* All other public functions for camera*/
/* Example method we want to implement based on tags/traits */
const glm::mat4& getProjectionMatrix();
private:
std::unique_ptr<AbstractImpl> _this;
};
Camera.cpp
#include "Camera.h"
#include "Impl.h"
Camera::Camera(AbstractImpl *impl) : _this(impl){}
Camera::~Camera() = default;
/*And all implementations*/
/*PIMPL Facade function*/
const glm::mat4& Camera::getProjectionMatrix(){
return _this->getProjectionMatrix();
}
Impl.h
namespace Projection{
struct Ortographic{};
struct Perspective{};
}
namespace Behaviour{
struct FirstPerson{};
struct FreeLook{};
}
/* Abstract base class for all different implementations */
class Camera::AbstractImpl
{
public:
/* Contains all PIMPL-Idiom members of camera */
/* Contains the pure virtual function for our trait/tag-based method */
virtual const glm::mat4& getProjectionMatrix() = 0;
};
template<typename t_projection, typename t_behaviour>
class Camera::Impl : public Camera::AbstractImpl
{
public:
Impl(){}
const glm::mat4& getProjectionMatrix(){
return getProjectionMatrix(t_projection{});
}
const glm::mat4& getProjectionMatrix(Projection::Ortographic){
/* Code for Ortographic Projection */
}
const glm::mat4& getProjectionMatrix(Projection::Perspective){
/* Code for Perspective Projection */
}
};
而這個現在可以使用的方式
Camera * c = new Camera(new Camera::Impl<Projection::Ortographic,Behaviour::FreeLook>());
c->getProjectionMatrix();
好處:除了AbstractImpl子類的明顯動態類型之外,這應該大部分是靜態類型的。在運行時可以切換相機的具體實現。新特徵可以很容易地添加。也可以使用這種方法,因爲所需要的只是添加AbstractImpl的子類。
缺點:調試會變得非常痛苦。在嘗試這個過程中,我一直意識到,儘管在編譯/鏈接期間很多錯誤可能會顯示出來,但仍然很難找到問題。
我會欣賞一些關於解決方案4的反饋,以及由於Yaaks實現而添加的。我是否正確使用了您的建議?我是否曾經錯誤地監督過某件事情或使用過模板 - 因爲我以前沒有使用它們。
您能否解釋接受的解決方案如何解決這個問題?可悲的是,答案並不包含任何討論,我真的不明白這裏的答案與多重(鑽石)繼承有什麼關係? 編輯︰看起來像這個問題有關的評論已被刪除 – NoxMortem 2014-11-03 18:57:58