C:从基类型指针确定派生类型

背景:

有关我需要解决的类似情况,请参阅this question in the C++ FAQ,但使用命名构造函数.

我有一个Base类,B类.

我有一个来自B,类D的Derived类,它通过函数,成员和额外的内存分配添加了额外的功能.

通过不做任何操作或从特定于D类的虚函数返回默认值和nullptrs,在B类中多态支持附加功能.

B类使用公共静态工厂方法来构造所有受保护的构造函数. (见:Named Constructor Idiom)

D类使用公共静态工厂方法来构造所有受保护的构造函数,这些构造函数的命名与B类不同,在B类中不可用.

稍后,创建一个新的接口类,类A.这个类有一个接口,这样来自类A的派生类必须有一个getter函数和一个setter函数,它们都需要一个指向B类的指针,但动态值可以是无论是B级还是D级

题:

我想派生类A并为类B创建一个复制构造函数,赋值运算符和/或setter,但是因为类A只将其成员公开为BI类型的对象,所以无法确定对象是否返回是B级或D级.

如何仅使用公共接口正确实现上述内容而不会导致切片或内存问题(包括如果设置错误并需要更改)?

可能的解决方案?:

我很想尝试几种选择:

1)在类B中创建一个成员以及声明对象类型的所有派生类型:

if(getB()->GetType() == "D") {
    //Call D::CreateD(...)
} else if(getB()->GetType() == "B") {
    //Call B::CreateB(...)
}

2)动态转换为派生类型并检查失败:

if(dynamic_cast<D*>(getB()) == nullptr) {
    //Call B::CreateB(...)
} else {
    //Call D::CreateD(...)
}

3)使用特定于D类的虚方法,我知道当在B类对象上使用时返回nullptr:

if(getB()->VirtualMethodSpecificToClassD() == nullptr) {
    //Call B::CreateB(...)
} else {
    //Call D::CreateD(...)
}

这三种情况都有代码味道:

>导致“else-if-heimers”.
>不太确定这实际上是否有效.
>违反“代码到接口而非实现”的良好做法.

最佳答案 根据zneak的评论,我认为如果你使用的是工厂方法和私人施工人员,那么就没有什么比

 virtual B* copy() const { return new B(*this); /* calls private B::B(const B&) */ }

B类中的方法,在D类中重写(返回一个新的D * – 在C中特别允许使用协变返回类型).

然后你的A拷贝构造函数可以做类似的事情

A::A(const A& other) : b(other.getB()->copy()) {}

它应该工作正常.

另一方面,如果你更愿意使用你建议的解决方案之一,我认为第一个是最不刺激的 – 虽然我会选择枚举而不是字符串,所以你可以使用简单的switch语句而不是字符串比较.我相信LLVM使用类似这样的东西进行“动态转换”,以避免C RTTI开销.

点赞