将继承转换为模板
convert inheritance to templates
我有一个使用继承的 C++11 项目。这是小片段:
class ICountable{
public:
virtual ~ICountable(){}
unsigned getCount() const{
return _getCount();
}
bool isEmpty() const{
return _getCount() == 0;
}
private:
virtual unsigned _getCount() const = 0;
};
假设我们有一些 LinkList
继承自 ICountable
并实现 _getCount()
。然后你可以像这样制作函数:
void doSomething(ICountable &countable){
if (countable.isEmpty())
doSomethingElse();
}
...
LinkList ll;
doSomething(ll);
这一切都很好,但必须有另一种方法来做到这一切:
template <typename T>
void doSomething(T countable){
if (countable.isEmpty())
doSomethingElse();
}
...
LinkList ll;
doSomething(ll); // we do not even need to add <>
模板方式更快而且可能更容易实现。 std::vector
和std::deque
也是这样。
但是,如何避免代码重复 - 函数 isEmpty()
必须粘贴在所有 "list things".
中
我可以想象预处理器 #include
或...
我可以想象 decorator
-like class 可以实现所有这些糖方法并代理其他方法:
template <typename T>
class List{
T list;
public:
unsigned getCount() const{
return list.getCount();
}
bool isEmpty() const{
return list.getCount() == 0;
}
...
}
如果没有运行时多态性,继承或模板哪个更好?
有没有更好的方法来避免代码重复?
使用模板,您只需隐含接口即可。您编写一个函数模板,并要求您传入的类型符合您的条件。在这种情况下,只需:
template <typename T>
void doSomething(T const& countable) {
if (countable.empty()) {
doSomethingElse();
}
}
所有 C++ 标准容器都有一个 empty()
方法,您可能比复制自己编写的容器的标准接口做得更糟。此函数已经适用于 vector
和 list
以及 string
和 ...,所有这些都没有任何动态调度开销,也没有必须从某些接口继承的 OOP 要求。
如果类型具有您需要的接口,泛型编程就可以工作——它们不需要继承所有正确命名的接口来获取它们。
有一个 C++ 特性尚未正式添加到标准中(但已经存在了很长一段时间)首先称为 Concepts that would essentially let you declare your doSomething(T countable)
to have the requirement that T
implement a certain interface (ie, that it has an isEmpty()
function). Until then, you can't avoid polymorphism if what you want is to avoid code duplication by sharing a set of common functions (re: interface) between different classes. That's one of the main reasons to use polymorphism。
话虽如此,您正在寻找像评论中提到的 Igor Tandetnik 这样的 Curiously Recurring Template Pattern。这允许您在 class 和 之间共享公共代码,而不会 虚拟函数的运行时损失(运行时多态性)。使用 CRTP 可以让编译器为您做这些事情。
使用简单的基础class:
template <typename T>
struct ICountable {
bool isEmpty() const { return static_cast<T*>(this)->_getCount() == 0; }
};
然后是你的导出列表:
class LinkedList : public ICountable<LinkedList> {
int _getCount() const { return size; }
};
以这种方式从 ICountable
派生的任何 class 现在将有一个 isEmpty()
方法,该方法使用特定于实现的 _getCount()
方法,而不使用虚函数.
我有一个使用继承的 C++11 项目。这是小片段:
class ICountable{
public:
virtual ~ICountable(){}
unsigned getCount() const{
return _getCount();
}
bool isEmpty() const{
return _getCount() == 0;
}
private:
virtual unsigned _getCount() const = 0;
};
假设我们有一些 LinkList
继承自 ICountable
并实现 _getCount()
。然后你可以像这样制作函数:
void doSomething(ICountable &countable){
if (countable.isEmpty())
doSomethingElse();
}
...
LinkList ll;
doSomething(ll);
这一切都很好,但必须有另一种方法来做到这一切:
template <typename T>
void doSomething(T countable){
if (countable.isEmpty())
doSomethingElse();
}
...
LinkList ll;
doSomething(ll); // we do not even need to add <>
模板方式更快而且可能更容易实现。 std::vector
和std::deque
也是这样。
但是,如何避免代码重复 - 函数 isEmpty()
必须粘贴在所有 "list things".
我可以想象预处理器 #include
或...
我可以想象 decorator
-like class 可以实现所有这些糖方法并代理其他方法:
template <typename T>
class List{
T list;
public:
unsigned getCount() const{
return list.getCount();
}
bool isEmpty() const{
return list.getCount() == 0;
}
...
}
如果没有运行时多态性,继承或模板哪个更好?
有没有更好的方法来避免代码重复?
使用模板,您只需隐含接口即可。您编写一个函数模板,并要求您传入的类型符合您的条件。在这种情况下,只需:
template <typename T>
void doSomething(T const& countable) {
if (countable.empty()) {
doSomethingElse();
}
}
所有 C++ 标准容器都有一个 empty()
方法,您可能比复制自己编写的容器的标准接口做得更糟。此函数已经适用于 vector
和 list
以及 string
和 ...,所有这些都没有任何动态调度开销,也没有必须从某些接口继承的 OOP 要求。
如果类型具有您需要的接口,泛型编程就可以工作——它们不需要继承所有正确命名的接口来获取它们。
有一个 C++ 特性尚未正式添加到标准中(但已经存在了很长一段时间)首先称为 Concepts that would essentially let you declare your doSomething(T countable)
to have the requirement that T
implement a certain interface (ie, that it has an isEmpty()
function). Until then, you can't avoid polymorphism if what you want is to avoid code duplication by sharing a set of common functions (re: interface) between different classes. That's one of the main reasons to use polymorphism。
话虽如此,您正在寻找像评论中提到的 Igor Tandetnik 这样的 Curiously Recurring Template Pattern。这允许您在 class 和 之间共享公共代码,而不会 虚拟函数的运行时损失(运行时多态性)。使用 CRTP 可以让编译器为您做这些事情。
使用简单的基础class:
template <typename T>
struct ICountable {
bool isEmpty() const { return static_cast<T*>(this)->_getCount() == 0; }
};
然后是你的导出列表:
class LinkedList : public ICountable<LinkedList> {
int _getCount() const { return size; }
};
以这种方式从 ICountable
派生的任何 class 现在将有一个 isEmpty()
方法,该方法使用特定于实现的 _getCount()
方法,而不使用虚函数.