使用虚函数和C ++继承机制与使用模板和boost概念之间的关系是什么?
似乎有可能的重叠。也就是说,似乎可以用任何一种方法实现多态行为。那么,什么时候支持一个而不是另一个呢?
我提出这个问题的原因是因为我有一个模板化的容器,容器本身具有层次关系。我想编写使用这些容器的算法,而不关心它是哪个特定的容器。此外,一些算法将从知道模板类型满足某些概念(例如,可比较)中受益。
因此,一方面,我希望容器具有多态性。另一方面,如果我想正确实现某些算法,我仍然需要使用概念。什么是初级开发人员?
答案 0 :(得分:6)
我认为概念是一种元界面。他们按照自己的能力对类型进行分类。下一个C ++版本提供本机概念。直到我遇到C ++ 1x的概念以及它们如何允许将不同但不相关的类型组合在一起时,我才理解它。想象一下,你有一个Range
界面。您可以通过两种方式对其进行建模。一个是子类型关系:
class Range {
virtual Iterator * begin() = 0;
virtual Iterator * end() = 0;
virtual size_t size() = 0;
};
当然,从中派生的每个类都实现了Range接口,并且可以与您的函数一起使用。但是现在你看到它是有限的。阵列怎么样?这也是一个范围!
T t[N];
begin() => t
end() => t + size()
size() => N
遗憾的是,您无法从实现该接口的Range类派生数组。您需要一个额外的方法(重载)。那么第三方容器呢?您的库的用户可能希望将他们的容器与您的功能一起使用。但他无法改变其容器的定义。在这里,概念进入游戏:
auto concept Range<typename T> {
typename iterator;
iterator T::begin();
iterator T::end();
size_t T::size();
}
现在,您说一些类型的支持操作,如果T
具有适当的成员函数,则可以实现。在您的库中,您将编写泛型函数。这允许您接受任何类型的,只要它支持所需的操作:
template<Range R>
void assign(R const& r) {
... iterate from r.begin() to r.end().
}
这是一种很好的替代性。 任何类型都符合遵守概念的法案,而不仅仅是那些积极实施某些界面的类型。下一个C ++标准更进一步:它定义了一个Container
概念,它将适用于普通数组(通过一些caled 概念图来定义一些类型如何适合某些概念)和其他,现有的标准容器。
我提出这个问题的原因是因为我有一个模板化的容器,容器本身具有层次关系。我想编写使用这些容器的算法,而不关心它是哪个特定的容器。此外,一些算法将从知道模板类型满足某些概念(例如,可比较)中受益。
您实际上可以使用模板进行这两项操作。您可以继续使用层次关系来共享代码,然后以通用方式编写算法。例如,传达您的容器具有可比性。这类似于标准的随机访问/转发/输出/输入迭代器类别:
// tag types for the comparator cagetory
struct not_comparable { };
struct basic_comparable : not_comparable { };
template<typename T>
class MyVector : public BasicContainer<T> {
typedef basic_comparable comparator_kind;
};
/* Container concept */
T::comparator_kind: comparator category
实际上,这是一种合理的简单方法。现在您可以调用一个函数,它将转发到正确的实现。
template<typename Container>
void takesAdvantage(Container const& c) {
takesAdvantageOfCompare(c, typename Container::comparator_kind());
}
// implementation for basic_comparable containers
template<typename Container>
void takesAdvantage(Container const& c, basic_comparable) {
...
}
// implementation for not_comparable containers
template<typename Container>
void takesAdvantage(Container const& c, not_comparable) {
...
}
实际上可以使用不同的技术来实现它。另一种方法是使用boost::enable_if
每次启用或禁用不同的实现。
答案 1 :(得分:1)
是的,两种机制都可以实现多态行为。实际上,两者都是也称为多态性。
虚函数为您提供动态多态(因为它是在运行时决定的),而模板为您提供静态多态(一切都在编译时决定)。
这应该回答哪个更喜欢的问题。只要有可能,就更愿意将工作转移到编译时。因此,当您可以使用它时,使用模板来解决您的多态性需求。当这不可能时(因为你需要使用运行时类型信息,因为在编译时不知道确切的类型),所以回退到动态多态。
(当然可能有其他原因偏好其中一个。特别是,模板要求你将大量代码移动到头文件中,这可能是也可能不是问题,编译速度往往会受到影响,也可能是也可能不是问题。)
答案 2 :(得分:0)
如果可以在编译时做出决定,请使用模板。否则使用继承和虚函数。
答案 3 :(得分:0)
在这种特殊情况下,您可以执行类似
的操作template<typename T>
class ContainerBase{};
template<typename T>
class ContainerDerived : public ContainerBase<T> {};
由于每个“容器”类型对于每个模板类型都是唯一的,因此没有理由不能将每个容器类型的成员函数专门用于模板化类型的特征。
答案 4 :(得分:0)
作为编译时和运行时多态之间差异的一个简单示例,请考虑以下代码:
template<typename tType>
struct compileTimePolymorphism
{ };
// compile time polymorphism,
// you can describe a behavior on some object type
// through the template, but you cannot interchange
// the templates
compileTimePolymorphism<int> l_intTemplate;
compileTimePolymorphism<float> l_floatTemplate;
compileTimePolymorphism *l_templatePointer; // ???? impossible
struct A {};
struct B : public A{};
struct C : public A{};
// runtime polymorphism
// you can interchange objects of different type
// by treating them like the parent
B l_B;
C l_C:
A *l_A = &l_B;
l_A = &l_C;
当一个对象的行为依赖于某个其他对象时,编译时多态性是一个很好的解决方案。在需要更改对象行为的情况下,运行时多态性是必要的。
可以通过定义多态的模板来组合这两个:
template<typename tType>
struct myContainer : public tType
{};
那么问题就是容器的行为需要改变的地方(运行时多态性),以及行为取决于它包含的对象(编译时多态)。