Sean Parent 的演讲,Inheritance is the base class of evil , 表示多态性不是类型的属性,而是如何使用它的属性。作为一个经验法则,不要使用继承来实现接口(interface)。这样做的许多好处之一是类的去虚拟化,这些类仅仅因为它们实现了一个接口(interface)而具有虚函数。这是一个例子:
class Drawable
{
public:
virtual void draw() = 0;
};
class DrawA : public Drawable
{
public:
void draw() override{//do something}
};
class UseDrawable
{
public:
void do(){mDraw->draw();}
Drawable* mDraw;
};
这里,而不是 UseDrawable
需要mDraw
成为 Drawable*
,你可以让它使用一个类型删除的类,它可以包裹任何实现名为 draw
的成员的类.所以,像 boost::type_erasure::any
具有适当的定义。那样,DrawA
不需要从 Drawable
继承- 多态性真的是UseDrawable
s 要求,而不是 DrawA
的真正属性.
我正在尝试按照这个原则重构一些代码。我有一个抽象类 ModelInterface
和两个具体的类ModelA
和 ModelB
继承自 ModelInterface
.按照 Sean 的建议,不强制 ModelA
是有意义的。和 ModelB
进入继承层次结构,并在需要满足 ModelInterface
建模的概念的类的位置简单地使用类型删除.
现在,我的问题是我的代码中的大多数地方当前使用 ModelInterface
也可以通过基于运行时配置文件构造适当的对象来做到这一点。目前,工厂将new
一个合适的对象并返回一个 ModelInterface*
.如果我重构代码以在代码中的这些位置使用类型删除的概念(比如 boost::type_erasure::any<implement ModelInterface>
),我如何在运行时构造这样的对象?将ModelA
和 ModelB
还需要启用 RTTI 的类(class)吗?或者我可以在没有 RTTI 信息的情况下以某种方式在工厂构建和使用它们吗?
(使用 RTTI,我可以拥有一个抽象类,例如 FactoryConstructible
,并使用 dynamic_cast<void*>
来获得最终类型。)
最佳答案
键入删除 101:
第 1 步:制作隐藏细节的常规(或半常规移动)类型。
struct exposed_type;
这个类公开了你想要支持的概念。复制、移动、销毁、等于、总顺序、哈希和/或您需要支持的任何自定义概念。
struct exposed_type {
exposed_type(exposed_type const&);
exposed_type(exposed_type&&);
friend bool operator<(exposed_type const&, exposed_type const&);
friend std::size_t hash(exposed_type const&);
// etc
};
其中许多概念可以从您当前基于继承的解决方案中的纯虚拟接口(interface)方法粗略映射。
在表达概念的 Regular 类型中创建非虚拟方法。复制/分配复制等。
第 2 步:编写类型删除助手。
struct internal_interface;
这里有纯虚拟接口(interface)。 clone()
用于复制等
struct internal_interface {
virtual ~internal_interface() {}
virtual internal_interface* clone() const = 0;
virtual int cmp( internal_interface const& o ) const = 0;
virtual std::size_t get_hash() const = 0;
// etc
virtual std::type_info const* my_type_info() const = 0;
};
在上面的常规类型中存储一个智能指针1。
struct exposed_type {
std::unique_ptr<internal_interface> upImpl;
将常规方法转发给助手。例如:
exposed_type::exposed_type( exposed_type const& o ):
upImpl( o.upImpl?o.upImpl->clone():nullptr )
{}
exposed_type::exposed_type( exposed_type&& o )=default;
第 3 步:编写类型删除实现。这是一个template
类,它存储一个T
并从helper 继承,并将接口(interface)转发给T
。如果没有找到 adl 自由函数,则使用在默认实现中使用方法的自由函数(有点像 std::begin
)。
// used if ADL does not find a hash:
template<class T>
std::size_t hash( T const& t ) {
return std::hash<T>{}(t);
}
template<class T>
struct internal_impl:internal_interface {
T t;
virtual ~internal_impl() {}
virtual internal_impl* clone() const {
return new internal_impl{t};
}
virtual int cmp( internal_interface const& o ) const {
if (auto* po = dynamic_cast<internal_interface const*>(&o))
{
if (t < *po) return -1;
if (*po < t) return 1;
return 0;
}
if (my_type_info()->before(*o.my_type_info()) return -1;
if (o.my_type_info()->before(*my_type_info()) return 1;
ASSERT(FALSE);
return 0;
}
virtual std::size_t get_hash() const {
return hash(t);
}
// etc
std::type_info const* my_type_info() const {
return std::addressof( typeid(T) ); // note, static type, not dynamic
}
};
第 4 步:向常规类型添加一个构造函数,该构造函数接受一个 T
并从中构造一个类型删除实现,并将其填充到其指向帮助程序的智能指针中。
template<class T,
// SFINAE block using this ctor as a copy/move ctor:
std::enable_if_t<!std::is_same<exposed_type, std::decay_t<T>>::value, int>* =nullptr
>
exposed_type( T&& t ):
upImpl( new internal_impl<std::decay_t<T>>{std::forward<T>(t)} )
{}
完成所有这些工作后,您现在拥有了具有常规(或半常规)值类型的非侵入式多态系统。
您的工厂函数返回常规类型。
查看 std::function
的示例实现以了解这是否已完全完成。
1唯一和共享都是不错的选择,具体取决于您是要存储不可变/写入数据时复制,还是手动克隆。
关于c++ - 澄清 Sean Parent 的谈话 "Inheritance is the base class of evil",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26199126/