背景
我只是尝试探索一些可用的模式和巧妙的使用可能性(在c++中)。有很多关于如何使用多个虚拟继承或pimpl成语的信息,也有关于桥梁或嵌套泛化的很好的主题,但是我没有找到一个很好的主题。
我知道类似的问题,例如:
但我认为这不是一个重复,值得它自己的答案。我的方法可能是绝对愚蠢的,所以请告诉我一些有用的方向。这不是家庭作业,因此对答案/语言/范例没有任何限制,但为了不讨论个人偏好,请试着推理为什么某个特定模式/解决方案是有效的,或者因为其他提出的解决方案是错误的/容易出错的/.。
问题
实现以下结构(可能是组合而不是继承)的好方法是什么?如果重用代码、更容易读/写、更容易扩展是主要的重点(也许您可以考虑其他问题),那么不同的答案可能是有效的。
我在当前实现中看到的主要问题是,这一点根本无法扩展,因为我们为获得了一个类,每个要建模的属性的值的每个组合都是这样。为了简单起见,为了简单起见,我使用了一个带有属性投影(FirstPerson或FreeLook)和行为的相机,但是对于属性为P1、P2、P3、P4的任何实体A,它们都可以是多个子类型中的一个,P1.2,.P2=>P2.1,P2.2,.
投影应确定执行
virtual const glm::mat4& getProjectionMatrix() = 0;而行为则应界定
virtual const glm::vec3& getForwardVector() = 0;可能的解决方案1:将pimpl成语与多个虚拟继承相结合
Sidenote:我使用名称_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的数据器和函数,这可能是必要的(假设overriden虚拟方法的返回值取决于私有数据成员的值)。
而且,这看起来像一个实现,代码生成器可以很好地支持它,因为您只需要对一个新子类进行正确的继承。
可能的解决办法2:组成
我能想到的另一个解决方案就是使用构图。
Camera.h
class Camera{
protected:
ProjectionType _projectionType;
CameraBehaviour _cameraBehaviour;
private:
class Impl;
std::unique_ptr<Impl> _this;
};如果ProjectionType或CameraBehaviour子类依赖于相机的任何值::Impl,则需要传递实例,我认为这会使代码非常混乱。另一方面,我们最终得到的类要少得多,因为每个projectionType需要一个类,每个cameraBehaviour需要一个类。如果我们有许多类型和行为的可能值,这可能是解决方案。
可能的解决方案3:继承
当然,我们可以只使用相机的子类。在这种情况下,可能使用pimpl,也可能不使用pimpl,因为所有需要受保护的visibilty的数据成员都不应该是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类也是如此。在这里,虚拟方法将作为所有透视图/轨道图实现多次.类共享方法的相同实现。
virtual const glm::mat4& getProjectionMatrix() = 0; 当所有FreeLook/FirstPerson类共享相同的实现时,
virtual const glm::vec3& getForwardVector() = 0;谢谢你花时间,我希望这是一个很好的问题,因为我已经对它作了相当多的思考,希望它对许多人来说是有趣的:)
编辑:如果有人能为这个问题想出一个更好的标题,请随意编辑,我很难找到一个好的标题。
可能的解决方案4:模板(添加4.11 19:00 GMT+2)
基于Yakk的回答,我研究了一个模板解决方案。
Camera.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的实现而添加了这些信息。我正确地使用了你的建议吗?我是否错误地监督了某些事情或错误地使用了模板--因为我以前没有使用过它们。
发布于 2014-11-03 19:08:04
struct ortographic_tag {};
struct perspective_tag {};
struct first_person_tag {};
struct freelook_tag {};
template<class projection_tag, class location_tag>
struct camera_impl : Camera::AbstractImpl {
virtual const glm::mat4& getProjectionMatrix() override {
return get_projection_matrix(*this, projection_tag{});
}
virtual const glm::vec3& getForwardVector() override {
return get_forward_vector(*this, location_tag{});
}
};
template<class camera>
const glm::mat4& get_projection_matrix( camera const& c, ortographic_tag ) {
// code
}
template<class camera>
const glm::mat4& get_projection_matrix( camera const& c, perspective_tag ) {
// code
}
template<class camera>
const glm::vec3& get_forward_vector( camera const& c, first_person_tag ) {
// code
}
template<class camera>
const glm::vec3& get_forward_vector( camera const& c, freelook_tag ) {
// code
}camera_impl< x, y >将各种虚拟方法转发给非虚拟函数。
如果您需要根据投影/位置在_impl中存储不同的数据,则可以执行以下一些特性:
template<class Tag>
struct camera_data;并将camera_data<projection_tag>和camera_data<location_tag>存储在_impl中,但是如果我们要这么做的话,我会重新考虑这个方法。
我们甚至可以更进一步,让我们的impl将每个标记传递给每个空闲函数,让空闲函数计算出需要关心的标记。
template<class... Tags>
struct many_tags {};
template<class T0, class...Tags>
struct many_tags:T0, many_tags<Tags...> {};传递many_tags<a, b, c>,如果它们只是在标记a上重载,它将匹配。如果有一个以上的过载,你会有歧义,除非他们去工作去处理它自己。如果他们想要a或b,他们必须使用is_base_of编写自定义SFINAE,或者等待概念精简。
https://stackoverflow.com/questions/26720521
复制相似问题