11

这样更容易解释一个例子,

class base {
//....
}

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

在我的库中,有一个基类的指针。库的用户必须从基类或派生类派生类,并将指针分配给该类。

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

4

6 回答 6

15

我对提议的编译时 x 运行时解决方案有一些评论。他们除了在什么时候被评估,is_base_of而且dynamic_cast有不同的要求,他们的答案也可以不同。

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

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

(3) dynamic_cast并且可以根据继承的类型( vs或)is_base_of给出不同的答案(或者一个可以编译而另一个不能编译)。例如考虑:publicprotectedprivate

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;

我们有

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.

实际上最后一行在 GCC ( error: 'B' is an inaccessible base of 'D2') 中产生了一个编译器错误。VS2010 会编译它(只产生类似于 GCC 错误消息的警告)。

(4)通过异常处理技巧可以放宽对多态类的要求。考虑:

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_ofdynamic_cast和(在下面的更新中提到的重命名后变得更加明显)总是返回一个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.

以下链接中提供了有关此技巧的一些过时参考:

第 1部分、第 2 部分代码

免责声明:is_unambiguous_public_base_of以上的实施只是一个表达意见的草案,并没有正确处理constvolatile资格。

更新:在这篇文章的先前版本中is_unambiguous_public_base_of被命名my_dynamic_cast,这是一个混乱的根源。所以我把它重命名为一个更有意义的名字。(感谢简赫尔曼。)

于 2013-08-07T10:25:54.457 回答
2

您可以使用dynamic_cast

if (dynamic_cast<DerivedClass*>(ptr)) {
    std::cout << "Class is derived from DerivedClass.";
}
于 2013-08-07T09:16:28.013 回答
1

我认为这个问题的答案非常困难。当然有std::is_base_ofdynamic_cast。两者都为您提供了一些非常有限的信息。第三个选项是函数重载。使用所有这些技术,您可以在代码中选择应该执行的特殊路径。

std::is_base_of可以在布尔上下文中解释,它来自std::true_typestd::false_type。这一事实使得可以将其用作函数的参数并通过函数重载使用编译时多态性。第一个示例展示了如何在布尔上下文中使用它,但您没有任何进一步的特定类型信息。因此,在大多数情况下编译都会失败(有关进一步的描述,请参见此处):

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

于 2013-08-07T13:37:17.830 回答
1

检查类是否派生自特定类(编译时)

您可以使用std::is_base_of

#include <type_traits>

....

const bool isBase = std::is_base_of<base, TheOtherClass>::value;

isBase如果TheOtherClass派生自则为真base

于 2013-08-07T09:14:47.283 回答
0

您可以通过多种方式做到这一点。正如其他人指出的那样,最常见的是dynamic_cast<>std::is_base_of。后者在编译时使用,而dynamic_cast<>可以在运行时使用。 但是dynamic_cast<>当您的源类是多态的(即至少具有一个虚函数 - 它可以是方法或其析构函数)时才有效。如果不是,编译器将触发错误。

于 2013-08-07T09:24:54.530 回答
0

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

于 2013-08-07T09:15:37.117 回答