检查类是否派生自特定类(编译,运行时两个答案都可用)

时间:2013-08-07 09:10:31

标签: c++ class c++11 encapsulation

更容易解释一个例子,

class base {
//....
}

class derived1 : public base {
//...
}

在我的库中,有一个基类指针。库的用户必须创建从base或derived1派生的类,并分配指向该类的指针。

如何检查从哪个类派生的用户定义类?

6 个答案:

答案 0 :(得分:9)

我对拟议的编译时x运行时解决方案有一些评论。除了评估它们之外,is_base_ofdynamic_cast有不同的要求,它们的答案也可能不同。

(1)首先(正如其他人所指出的)使用dynamic_cast,基类和派生类必须是多态的(必须至少有一个virtual方法)。 is_base_of不要求类型具有多态性。

(2) is_base_of的操作数都是类型,而dynamic_cast需要一个类型(在< >内)和一个对象(在{{1}内) })。

(3) ( )dynamic_cast可以给出不同的答案(或者一个可以编译,而另一个不能),具体取决于继承的类型({{1 } {vs is_base_ofpublic)。例如,考虑:

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_ofdynamic_cast。两者都为您提供了一些非常有限的信息。第三个选项是函数重载。使用所有这些技术,您可以在代码中选择应该执行的特殊路径。

std::is_base_of可以在布尔上下文中解释,它来自std::true_typestd::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)个派生类才能很好地扩展。

最后但并非最不重要的是,您应该考虑您的课程设计,并在basederived1或其他课程中确定哪些方法可以实现虚拟和实施。你应该明确地寻找strategy pattern

答案 4 :(得分:0)

如果库函数接受指向基类的指针,编译器将只接受指向从基类派生的类的指针。我的回答是采用经典方法,类型安全将处理它。根据我的经验,这种类型检查就足够了。拥有25年的行业经验,我质疑是否需要进行此项检查。也许不欢迎这样一个基本问题?我希望看到有必要做这种上调的理由。我从来没有这样做过。相反的,即我经常需要的演员。

答案 5 :(得分:0)

您可以通过多种方式完成此操作。其他人指出的最常见的是dynamic_cast<>std::is_base_of。后者在编译时使用,而dynamic_cast<>可以在运行时使用。 HOWEVER dynamic_cast<>只有在您的源类是多态时才会工作(即至少有一个虚函数 - 它可以是方法或其析构函数)。如果没有,编译器将触发错误。