如何将 shared_ptr 从一个父 class 投射到另一个父 class?
How do I cast a shared_ptr from one parent class to another parent class?
我正在重新设计我的游戏引擎以使用 smart-pointers
。我有一个 Object
class 一切都继承自它。我有一个可渲染的 GameObject,因此它继承自 IRenderable
(定义纯虚拟渲染函数的 class)并且 不继承自 Object。我有一个 RenderSystem
,它应该为场景中的所有 IRenderable 保存一个 shared_ptr
。
我遇到的问题是如何将我的 GameObject shared_ptr 转换为 RenderSystem 的 IRenderable?
我尝试过的想法:
- RenderSystem 使用 shared_ptr 的 GameObject 但这不起作用,因为它很危险,因为并非所有游戏对象都是可渲染的。
- 让 IRenderable 从 Object 继承,这应该允许我从它继承。这意味着 IRenderable 不再是一个接口,因为 Object 包含其他功能。
这完全可以用原始指针实现,因此,我觉得有一些方法可以用智能指针实现相同的结果
示例:
// Object.h
class Object : public enable_shared_from_this<Object> { ... }
// GameObject.h
class GameObject : public Object { ... }
// MeshRenderer.h
class MeshRenderer : public GameObject, IRenderable {
public:
void initialize()
{
// Not able to cast Object to IRenderable
RenderSystem::instance().addRenderable(getShared());
// AND
// Not able to cast Object to IRenderable
RenderSystem::instance().addRenderable(std::static_pointer_cast<IRenderable>(getShared()));
}
}
// RenderSystem.h
class RenderSystem
{
std::list<std::shared_ptr<IRenderable>> m_renderables;
public:
void addRenderable(std::shared_ptr<IRenderable> ptr)
{
m_renderables.push_back(ptr);
}
}
// main.cpp
...
auto meshRenderer = std::shared_ptr<MeshRenderer>();
...
像这样:
#include <memory>
// everything is an Object - yuk, but ok, if you wish...
struct Object : std::enable_shared_from_this<Object>
{
};
struct GameObject : Object
{
};
struct IRenderable
{
virtual void render() {};
};
struct RederableGameObject : GameObject, IRenderable
{
auto as_shared_renderable() -> std::shared_ptr<IRenderable>
{
// builds a new shared pointer to IRenderable which
// uses the same lifetime control block as me
return std::shared_ptr<IRenderable>
{
this->shared_from_this(), // where to get the control block
this // what to point to
};
}
};
文档:
见构造函数数(8)
http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
更新:
这是一个自由函数的起点,可以从任何对象中获取正确的 shared_pointer,前提是它最终公开派生自 std::enable_shared_from_this
#include <memory>
#include <type_traits>
namespace notstd
{
// stuff that I think *should be* std
using namespace std;
// a trait to determine whether class T is derived from template
// Tmpl<...>
template <typename T, template <class...> class Tmpl>
struct is_derived_from_template_impl
{
static std::false_type test(...);
template <typename...Us>
static std::true_type test(Tmpl<Us...> const &);
using result = decltype(test(std::declval<T>()));
};
template <typename T, template <class...> class Tmpl>
using is_derived_from_template = typename is_derived_from_template_impl<T, Tmpl>::result;
template <typename T, template <class...> class Tmpl>
constexpr auto is_derived_from_template_v = is_derived_from_template<T, Tmpl>::value;
// free function version of shared_from_this
template<class T>
auto shared_from(enable_shared_from_this<T>* p)
-> std::shared_ptr<T>
{
return p->shared_from_this();
}
// specific shared_ptr construction from type T
template<class T>
auto shared_from(T*p)
-> enable_if_t
<
is_derived_from_template_v
<
T,
enable_shared_from_this
>,
std::shared_ptr<T>
>
{
return std::shared_ptr<T>(p->shared_from_this(), p);
}
}
// everything is an Object - yuk, but ok, if you wish...
struct Object : std::enable_shared_from_this<Object>
{
};
struct GameObject : Object
{
};
struct IRenderable
{
virtual void render() {};
};
extern int emit(const char* str);
struct RederableGameObject : GameObject, IRenderable
{
auto as_shared_renderable() -> std::shared_ptr<RederableGameObject>
{
return notstd::shared_from(this);
}
auto as_shared_renderable() const -> std::shared_ptr<const RederableGameObject>
{
return notstd::shared_from(this);
}
void e() const {
emit("const");
}
void e() {
emit("mutable");
}
};
int main()
{
auto rgo = std::make_shared<RederableGameObject>();
// prove it works
auto p1 = rgo->as_shared_renderable();
// prove it works with a const object also
auto p2 = static_cast<const RederableGameObject&>(*rgo).as_shared_renderable();
p1->e();
p2->e();
}
您不能直接从 shared_ptr<Object>
到 shared_ptr<IRenderable>
执行 static_pointer_cast
,因为这些类型是不相关的。
但是您可以转换为派生类型,然后允许隐式转换为 shared_ptr<IRenderable>
:
auto p = std::static_pointer_cast<MeshRenderer>(getShared());
std::shared_ptr<IRenderer> q = p;
这会执行从第一个基 class 到派生类型的显式转换,然后再隐式转换到第二个基类型。这是静态允许的,因为已知派生类型与两个基相关,但不相关基之间的直接转换是不可能的。
我正在重新设计我的游戏引擎以使用 smart-pointers
。我有一个 Object
class 一切都继承自它。我有一个可渲染的 GameObject,因此它继承自 IRenderable
(定义纯虚拟渲染函数的 class)并且 不继承自 Object。我有一个 RenderSystem
,它应该为场景中的所有 IRenderable 保存一个 shared_ptr
。
我遇到的问题是如何将我的 GameObject shared_ptr 转换为 RenderSystem 的 IRenderable?
我尝试过的想法:
- RenderSystem 使用 shared_ptr 的 GameObject 但这不起作用,因为它很危险,因为并非所有游戏对象都是可渲染的。
- 让 IRenderable 从 Object 继承,这应该允许我从它继承。这意味着 IRenderable 不再是一个接口,因为 Object 包含其他功能。
这完全可以用原始指针实现,因此,我觉得有一些方法可以用智能指针实现相同的结果
示例:
// Object.h
class Object : public enable_shared_from_this<Object> { ... }
// GameObject.h
class GameObject : public Object { ... }
// MeshRenderer.h
class MeshRenderer : public GameObject, IRenderable {
public:
void initialize()
{
// Not able to cast Object to IRenderable
RenderSystem::instance().addRenderable(getShared());
// AND
// Not able to cast Object to IRenderable
RenderSystem::instance().addRenderable(std::static_pointer_cast<IRenderable>(getShared()));
}
}
// RenderSystem.h
class RenderSystem
{
std::list<std::shared_ptr<IRenderable>> m_renderables;
public:
void addRenderable(std::shared_ptr<IRenderable> ptr)
{
m_renderables.push_back(ptr);
}
}
// main.cpp
...
auto meshRenderer = std::shared_ptr<MeshRenderer>();
...
像这样:
#include <memory>
// everything is an Object - yuk, but ok, if you wish...
struct Object : std::enable_shared_from_this<Object>
{
};
struct GameObject : Object
{
};
struct IRenderable
{
virtual void render() {};
};
struct RederableGameObject : GameObject, IRenderable
{
auto as_shared_renderable() -> std::shared_ptr<IRenderable>
{
// builds a new shared pointer to IRenderable which
// uses the same lifetime control block as me
return std::shared_ptr<IRenderable>
{
this->shared_from_this(), // where to get the control block
this // what to point to
};
}
};
文档:
见构造函数数(8)
http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
更新:
这是一个自由函数的起点,可以从任何对象中获取正确的 shared_pointer,前提是它最终公开派生自 std::enable_shared_from_this
#include <memory>
#include <type_traits>
namespace notstd
{
// stuff that I think *should be* std
using namespace std;
// a trait to determine whether class T is derived from template
// Tmpl<...>
template <typename T, template <class...> class Tmpl>
struct is_derived_from_template_impl
{
static std::false_type test(...);
template <typename...Us>
static std::true_type test(Tmpl<Us...> const &);
using result = decltype(test(std::declval<T>()));
};
template <typename T, template <class...> class Tmpl>
using is_derived_from_template = typename is_derived_from_template_impl<T, Tmpl>::result;
template <typename T, template <class...> class Tmpl>
constexpr auto is_derived_from_template_v = is_derived_from_template<T, Tmpl>::value;
// free function version of shared_from_this
template<class T>
auto shared_from(enable_shared_from_this<T>* p)
-> std::shared_ptr<T>
{
return p->shared_from_this();
}
// specific shared_ptr construction from type T
template<class T>
auto shared_from(T*p)
-> enable_if_t
<
is_derived_from_template_v
<
T,
enable_shared_from_this
>,
std::shared_ptr<T>
>
{
return std::shared_ptr<T>(p->shared_from_this(), p);
}
}
// everything is an Object - yuk, but ok, if you wish...
struct Object : std::enable_shared_from_this<Object>
{
};
struct GameObject : Object
{
};
struct IRenderable
{
virtual void render() {};
};
extern int emit(const char* str);
struct RederableGameObject : GameObject, IRenderable
{
auto as_shared_renderable() -> std::shared_ptr<RederableGameObject>
{
return notstd::shared_from(this);
}
auto as_shared_renderable() const -> std::shared_ptr<const RederableGameObject>
{
return notstd::shared_from(this);
}
void e() const {
emit("const");
}
void e() {
emit("mutable");
}
};
int main()
{
auto rgo = std::make_shared<RederableGameObject>();
// prove it works
auto p1 = rgo->as_shared_renderable();
// prove it works with a const object also
auto p2 = static_cast<const RederableGameObject&>(*rgo).as_shared_renderable();
p1->e();
p2->e();
}
您不能直接从 shared_ptr<Object>
到 shared_ptr<IRenderable>
执行 static_pointer_cast
,因为这些类型是不相关的。
但是您可以转换为派生类型,然后允许隐式转换为 shared_ptr<IRenderable>
:
auto p = std::static_pointer_cast<MeshRenderer>(getShared());
std::shared_ptr<IRenderer> q = p;
这会执行从第一个基 class 到派生类型的显式转换,然后再隐式转换到第二个基类型。这是静态允许的,因为已知派生类型与两个基相关,但不相关基之间的直接转换是不可能的。