更容易解释一个例子,
class base {
//....
}
class derived1 : public base {
//...
}
在我的库中,有一个基类指针。库的用户必须创建从base或derived1派生的类,并分配指向该类的指针。
如何检查从哪个类派生的用户定义类?
答案 0 :(得分:9)
我对拟议的编译时x运行时解决方案有一些评论。除了评估它们之外,is_base_of
和dynamic_cast
有不同的要求,它们的答案也可能不同。
(1)首先(正如其他人所指出的)使用dynamic_cast
,基类和派生类必须是多态的(必须至少有一个virtual
方法)。 is_base_of
不要求类型具有多态性。
(2) is_base_of
的操作数都是类型,而dynamic_cast
需要一个类型(在< >
内)和一个对象(在{{1}内) })。
(3) ( )
和dynamic_cast
可以给出不同的答案(或者一个可以编译,而另一个不能),具体取决于继承的类型({{1 } {vs is_base_of
或public
)。例如,考虑:
protected
我们有
private
实际上,最后一行在GCC(struct B { virtual ~B() {} }; // polymorphic, so it can be used in a dynamic_cast
struct D1 : public B {}; // polymorphic by (public) inheritance
struct D2 : private B {}; // polymorphic by (private) inheritance
D1 d1;
D2 d2;
)中产生编译器错误。 VS2010确实编译它(只产生类似于GCC错误消息的警告)。
(4)可以通过异常处理技巧放宽对多态类的要求。考虑:
static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert(dynamic_cast<B*>(&d1));
assert(!dynamic_cast<B*>(&d2)); // Notice the negation.
然后我们
error: 'B' is an inaccessible base of 'D2'
值得一提的是struct B { }; // not polymorphic
struct D1 : public B {}; // not polymorphic
struct D2 : private B {}; // not polymorphic
D1 d1;
D2 d2;
template <typename B, typename D>
const B* is_unambiguous_public_base_of(const D* obj) {
try {
throw obj;
}
catch (const B* pb) {
return pb;
}
catch (...) {
}
return nullptr;
}
远远慢于static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert((is_unambiguous_public_base_of<B>(&d1)));
assert(!(is_unambiguous_public_base_of<B>(&d2))); // Notice the negation.
并且(在下面的更新中提到的重命名后,这变得更加明显)总是返回is_unambiguous_public_base_of
用于向下转换:
dynamic_cast
有关此技巧的过时参考是here。
免责声明:上述nullptr
的实施只是一个草案,可以说明问题并且它无法正确处理B* b1 = &d1;
assert(dynamic_cast<D1*>(b1)); // Requires D1 and B to be polymorphic.
assert(!(is_unambiguous_public_base_of<D1>(b1))); // Notice the negation.
和is_unambiguous_public_base_of
资格。
更新:在此帖子的先前版本中const
被命名为volatile
,这引起了混淆。所以我把它重命名为一个更有意义的名字。 (感谢Jan Herrmann。)
答案 1 :(得分:1)
检查类是否派生自特定类(编译时)
您可以使用std::is_base_of
:
#include <type_traits>
....
const bool isBase = std::is_base_of<base, TheOtherClass>::value;
如果isBase
来自TheOtherClass
,则 base
为真。
答案 2 :(得分:1)
您可以使用dynamic_cast。
if (dynamic_cast<DerivedClass*>(ptr)) {
std::cout << "Class is derived from DerivedClass.";
}
答案 3 :(得分:1)
我认为这个问题的答案是非常困难。当然有std::is_base_of
和dynamic_cast
。两者都为您提供了一些非常有限的信息。第三个选项是函数重载。使用所有这些技术,您可以在代码中选择应该执行的特殊路径。
std::is_base_of
可以在布尔上下文中解释,它来自std::true_type
或std::false_type
。这个事实使得它可以用作函数的参数,并通过函数重载使用编译时多态性。第一个示例显示如何在布尔上下文中使用它,但您没有任何进一步的特定类型信息。因此编译在大多数情况下都会失败(see here for a further description):
template<class T>
void do_it1(T const& t) {
if (std::is_base_of<T,derived1>::value) {
// we have a derived1
} else {
// we have a base
}
}
第二个版本是简单的函数重载。这里使用编译时多态,但丢失了所有运行时类型信息(除了使用虚函数和dynamic_cast
之外):
void do_it2(Base const& b) {
// we have a base your algorithm for base here
}
void do_it2(Derived2 const& d) {
// Derived algorithm here
}
现在第三个版本结合了两个:
template<class T>
void do_it3_impl(T const& t, std::true_type) {
// here t will be of a type derived from derived1
}
template<class T>
void do_it3_impl(T const& t,std::false_type) {
// here t will be of type not derived from derived1
}
template<class T>
void do_it_3(T const& t) {
do_it3_impl(t, std::is_base_of<T,derived1>()); // here we forward to our impl
}
第三个变体通常用于不使用运行时多态性的头文件库(搜索std::advance
进行抽样)。
现在到运行时多态。这里有dynaminc_cast
变体:
void do_it4(Base const* ptr)
if (derived1 const* obj = dynamic_cast<derived*>(ptr)) {
// here we have obj with type derived1*
} else {
// here we have only base
}
如果此变体速度不够快,您可以将onw强制转换为derived1:
class derived1;
class base {
// as above
public:
virtual derived1 const* to_derived1() const {
return 0;
}
};
class derived1
: public base
{
// ...
virtual derived1 const* to_derived1() const {
return this;
}
};
void do_it5(Base const* ptr)
if (derived1 const* obj = ptr->to_derived1() {
// here we have obj with type derived1*
} else {
// here we have only base
}
速度很快,但只有极少数(约1)个派生类才能很好地扩展。
最后但并非最不重要的是,您应该考虑您的课程设计,并在base
,derived1
或其他课程中确定哪些方法可以实现虚拟和实施。你应该明确地寻找strategy pattern。
答案 4 :(得分:0)
如果库函数接受指向基类的指针,编译器将只接受指向从基类派生的类的指针。我的回答是采用经典方法,类型安全将处理它。根据我的经验,这种类型检查就足够了。拥有25年的行业经验,我质疑是否需要进行此项检查。也许不欢迎这样一个基本问题?我希望看到有必要做这种上调的理由。我从来没有这样做过。相反的,即我经常需要的演员。
答案 5 :(得分:0)
您可以通过多种方式完成此操作。其他人指出的最常见的是dynamic_cast<>和std::is_base_of。后者在编译时使用,而dynamic_cast<>
可以在运行时使用。
HOWEVER ,dynamic_cast<>
只有在您的源类是多态时才会工作(即至少有一个虚函数 - 它可以是方法或其析构函数)。如果没有,编译器将触发错误。