背景:
参见 this question in the C++ FAQ对于我需要解决的类似情况,但使用命名构造函数。
我有一个基类,B 类
。
我有一个来自 B 的派生类,D 类
,它通过函数、成员和额外的内存分配添加了额外的功能。
class B
通过不执行任何操作或从特定于 class D< 的虚函数返回默认值和
.nullptrs
以多态方式支持附加功能
B 类
使用public static Factory Methods
来构造所有 protected 构造函数
。 (参见:Named Constructor Idiom)
D 类
使用public static Factory Methods
构造所有protected constructors
,这些构造函数的名称与 B 类不同,并且在 B 类中不可用.
一段时间后,创建了一个新的接口(interface)类,class A
。此类有一个接口(interface),这样来自 class A
的派生类必须有一个 getter 函数和一个 setter 函数,它们都需要一个 pointer to a class B
但动态值可以是 class B
或 class D
问题:
我想派生 class A
并为 class B
创建复制构造函数、赋值运算符和/或 setter,但是因为 class A
仅将其成员公开为 B
类型的对象 我无法确定返回的对象是 class B
还是 class D
.
我如何仅使用公共(public)接口(interface)正确实现上述内容而不会导致切片或内存问题(包括如果上述设置错误且需要更改)?
可能的解决方案?
我很想尝试几个选项:
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
的虚拟方法,我知道当在 class B
对象上使用时返回 nullptr
:
if(getB()->VirtualMethodSpecificToClassD() == nullptr) {
//Call B::CreateB(...)
} else {
//Call D::CreateD(...)
}
这三种情况都有代码味道:
- 导致“else-if-heimers”。
- 不太确定这是否真的有效。
- 违反了“代码到接口(interface)而不是实现”的良好做法。
最佳答案
根据 zneak
的评论,我认为如果您使用工厂方法和私有(private)构造函数,那么拥有一个
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 开销。
关于C++:从基类型指针确定派生类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19782588/