考虑一个存储一堆Date对象的类Calendar。 该日历旨在保存从Date继承的任何类型的对象的集合。我认为最好的方法是使用类模板,如
template<typename D> class Calendar{
...
}
但让我感到震惊的是,D现在实际上可以成为任何阶级。 我现在的问题是,如何确保D是日期对象的子类?
我知道如何做到这一点是Java,但我仍然不熟悉C ++语法。问题非常类似于某些集合只能采用实现Comparable的模板变量。然后标题看起来像
public class Calendar<D extends Date>{
...
}
--------------------编辑:------------------------- -----------------
template参数定义日历引用的实际日期。不同的日期类型以不同的格式表示同一天。例如,如果我创建Calendar<Gregorian>
,它将能够以另一种Date
格式,比如朱利安日历或任何其他日期格式拍摄日期,并以格里高利格式呈现。这样可以在不同日期格式的日历之间进行转换。所以,如果我有一个Calendar<Gregorian>
,我可以很容易地将其转换为Calendar<Julian>
。那么以下是可能的:
Calendar<Gregorian> cal;
std::cout << "These events are entered as dates in
the Gregorian calendar" << std::endl;
cal.add_event("Christmas", 12, 25);
cal.add_event("Gregorian new year", 1, 1);
std::cout << cal << std::endl;
std::cout << "----" << std::endl;
std::cout << "And printed out as Julian dates" << std::endl;
Calendar<Julian>(cal);
std::cout << cal<< std::endl;
和输出:
These events are entered as dates in the Gregorian calendar
2009-12-25 Christmas
2010-01-01 Gregorian new year
----
And printed out as Julian dates
2009-12-13 Christmas
2009-12-19 Gregorian new year
-------------新编辑:----------------------
现在最后一次编辑更有意义。我对格式有点不同意。
感谢所有答案。
我是第三年的计算机科学专业的学生,我会说我对OO和相关概念如Polymorphism等非常熟悉。这篇文章的目的是找出是否有办法在C ++中表达模板参数的条件与在Java中相同,并以简洁,优雅和直观的方式解决问题。
答案 0 :(得分:9)
我知道如何做到这一点是Java,但我仍然不熟悉C ++语法。问题非常类似于某些集合只能采用实现Comparable的模板变量。然后标题看起来像
public class Calendar<D extends Date>{
...
}
是的,这是同样的问题,在C ++中,通常通过忽略它来解决。为什么我们需要强制对象必须实现IComparable
?在Java中,由于其贫血型系统,它是必要的。没有这个约束,我们将无法比较对象。
在C ++中,规则是不同的。容器只需尝试来比较它们存储的对象,如果类型不支持它,则会出现编译错误。不需要接口或继承。
你通常会在Calendar
课程中做同样的事情。只需不强制“必须子类形式Date
约束。
相反,请指定类型必须公开的成员,以及应该从中获取的语义(如果有的话)。
例如,如果您的日历尝试执行以下操作,则对于日期对象d0
和d1
:
d0.getDay();
d0.getTime();
Time t = d0 - d1;
然后这些是应该支持的操作。任何支持这些操作的类都是一个有效的Date类,即使它没有任何的子类。
答案 1 :(得分:8)
您正在寻找的是模板参数的概念检查。这些已成为下一个C ++标准草案的一部分,但几周/几个月前又被抛弃了。
如果没有适当的语言概念,有一些库试图这样做,但是想要概念检查作为核心语言的一部分的原因是,如果没有语言支持,它或多或少是不可能实现的。
在你的具体例子中,这不应该太难。例如,您可以将一些特殊的typedef
放入基类并检查:
class date {
public:
typedef int is_derived_from_date;
};
template<typename D> class Calendar{
typedef typename D::is_derived_from_date blah;
...
};
另一种方法是选择在网络上浮动的任何is_derived<B,D>::result
模板元函数,并在Calender
类中对此进行静态检查。 Boost同时具有is_derived
元函数和静态断言。
说完这一切之后,我不得不质疑你的设计。普通的OO多态性有什么问题,你想使用模板的编译时多态?
答案 2 :(得分:3)
我认为您的问题可以在不使用模板的情况下解决。 D始终是Date的派生类,那么为什么不只是拥有Date对象的集合?
答案 3 :(得分:2)
在C ++中,这称为概念检查。在C ++中,一个常见的最佳实践是继承用于继承接口而不是实现。因此,您对D
是否继承Date
并不感兴趣,但D
是否具有您需要的Date
的界面元素,即它具有必要性这样做的好处是,您不需要将来的D
类继承自Date
,它们只需要实现某些功能。
概念检查已在C ++ 0x中删除,但您可以在Boost.ConceptCheck中找到它(Boost主站点为here)。
如果您确实希望强制D
继承自Date
,则可以将Boost.StaticAssert与Boost.TypeTraits结合使用来检查D
是否继承自Date
{{1}}。
答案 4 :(得分:1)
模板通常不需要继承/多态限制。模板旨在与满足给定要求的任何类型一起使用,无论基类型如何。
template <typename T>
T clone(const T& cloneable) {
return cloneable.create_clone();
}
此代码适用于支持create_clone()
操作的任何类型,不使用ICloneable
- 接口!
在您的情况下,此代码将允许使用任何类似日期的类型。
如果您想要基类多态,只需将模板保留并使用Date*
。
请注意,如果您确实想要进行模板测试,可以尝试将虚拟对象指针强制转换为Date*
,如果它不是Date
的派生,则会在编译时失败。但这通常不是模板代码的使用方式。
答案 5 :(得分:0)
如果您只想与Date对象进行交互,那么为什么不使用普通多态并只处理Date * -s?
如果你打算在Calendar中有一个日期集合,它可以包含不同Date子类的实例,那么我怀疑模板是否会起作用,除了多态之外你什么也没有帮助你。
对于模板,如果给定类型具有合适的接口,为什么它不适用于日历? (好吧,概念是为C ++ 0x计划的,但已经下降了,但他们的主要动机似乎是允许更清晰的与模板相关的错误消息。)