5

背景:

有关我需要解决的类似情况,但使用命名构造函数,请参阅C++ FAQ 中的这个问题。

我有一个基类,class B.

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

通过class B什么都不做或返回默认值以及nullptrs从特定于class D.

class B用于public static Factory Methods所有的建筑protected constructors。(请参阅:命名构造函数习语

class D用于public static Factory Methods所有与protected constructorsB 类命名不同且在 B 类中不可用的构造。

一段时间后,创建了一个新的接口类,class A. 此类具有一个接口,因此派生类class A必须具有一个 getter 函数和一个 setter 函数,它们都需要 apointer to a class B但动态值可以是class B或者class D

问题:

我想派生class A并创建一个复制构造函数、一个赋值运算符和/或一个 setter,class B但因为class A只将其成员公开为类型的对象,B我无法确定返回的对象是否为class Bor class 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)使用特定于class D我知道nullptrclass B对象上使用时返回的虚拟方法:

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

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

  1. 导致“else-if-heimers”。
  2. 不太确定这是否真的有效。
  3. 违反“代码到接口而不是实现”的良好做法。
4

1 回答 1

3

根据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 开销。

于 2013-11-05T08:06:11.247 回答