【发布时间】:2015-10-31 08:34:13
【问题描述】:
首先完整的免责声明:我没有编译这个示例代码,也没有编译真正的代码(嗯,至少完全部署)。我还在纠结这个问题。考虑到这一点,假设我们有这样的类结构:
一个超级基类,我们将使用该基类将实例存储在同一个容器中,以及一些“Facet”类,我们将使用多重继承来封装常见行为。
class Facet_A;
class Facet_B;
class Facet_C;
struct Facet_converter
{
Facet_A * facet_a;
Facet_B * facet_b;
Facet_C * facet_c;
};
class Super_base
{
public:
virtual ~Super_base() {}
virtual Facet_converter convert()=0;
virtual const Facet_converter convert()const=0; //Notice this const...
};
class Facet_A
{
private:
int value_a;
public:
virtual ~Facet_A() {}
Facet_A():value_a(0) {}
void set_value_a(int v) {value_a=v;}
int get_value_a() const {return value_a;}
};
class Facet_B
{
private:
float value_b;
public:
Facet_B():value_b(0) {}
virtual ~Facet_B() {}
void set_value_b(float v) {value_b=v;}
float get_value_b() const {return value_b;}
};
class Facet_C
{
private:
char value_c;
public:
Facet_C():value_c('a') {}
virtual ~Facet_C() {}
void set_value_c(char v) {value_c=v;}
char get_value_c() const {return value_c;}
};
从这些派生的所有类将始终:
- 使用 Super_base 作为公共基类,因此我们可以将它们存储在这些的向量中。
- 实现转换方法,该方法将返回一个 Facet_converter 对象,其中派生类的指针(共享、唯一、原始等)转换为特定方面(null,如果不适用)。
- 使用 Facet_A、Facet_B 或 Facet_C 作为基类,具体取决于他们尝试实现的内容。
客户端代码会执行类似...
std::vector<Super_base *> v;
//Fill super base with the good stuff.
//Let's use everything that has an integer!.
for(auto sb : v)
{
Facet_converter fc=sb->convert();
if(fc.facet_a)
{
//Do something with this integer like... std::cout<<fc.facet_a->get_value_a()<<std::endl;
}
}
//Let's use everything that has a float.
for(auto sb : v)
{
Facet_converter fc=sb->convert();
if(fc.facet_b)
{
//Do something with this float...
}
}
//Let's use everything that has a char.
for(auto sb : v)
{
Facet_converter fc=sb->convert();
if(fc.facet_c)
{
//You get the drift...
}
}
除了糟糕的设计(我已经厌倦了到处都是访客)这个特定的例子几乎是准系统,但你得到了我想要做的事情:在不使用 dynamic_cast 和“强制”编译器的情况下降低层次结构帮助(如果我尝试在“convert”方法中分配给非基类,它会冲我大喊大叫)。
所以,一个完全实现的类...
class Derived_numeric: //This one has a float and and int
public Super_base,
public Facet_A,
public Facet_B
{
///Blah blah blah blah
virtual Facet_converter convert()
{
Facet_converter result;
result.facet_a=this;
result.facet_b=this;
result.facet_c=nullptr; //Assume no constructor for the struct that initializes the method, not really the case.
return result;
}
virtual const Facet_converter convert()const
{
const Facet_converter result;
result.facet_a=this; //Booom!!!. Error, const Derived_numeric can't be caster to Facet_A because... it's const.
result.facet_b=this;
result.facet_c=nullptr;
return result;
}
}
问题就在 const convert 方法中。有一个 const 和一个非 const 方法,因为客户端代码可以使用 const 和非 const 对象,但是编译器不可能让我在不先进行 const 强制转换的情况下分配“const this”。
考虑到我提供了两种解决方案:
- const_casting this 指针在 const 方法中。
- 创建两个 Facet_converter 对象:Facet_converter 和 Facet_converter_const。它们完全相同,但一个具有 const 指针,另一个具有常规指针。让客户端代码使用他们需要的代码。
他们都遭受可怕的代码重复,因为代码几乎相同,只有少数细节发生变化。
我玩弄了仅实现 const 的想法,将“this”指针 const_casting 并基本上对方法的承诺撒谎。想要真正的 constness?,将 const 修饰符添加到 convert() 的结果中并完成它......看起来更容易,但太偷偷摸摸了。
我的问题是,我可以在不复制和粘贴代码并偷偷摸摸的情况下实现这个想法吗?请记住,我需要 const 和非 const(派生对象可能会通过使用构面来更改其状态,也可能不会)。
现在,请注意我不是在寻找“你的方法是错误的”或“我不知道你为什么要这样做”。这就是我要处理和了解的当前情况。我已经知道我可以使用双重调度,或者我可以将整个基类混为一谈以包含所有其他可能性……我只是在寻找它的替代方案。
【问题讨论】:
-
您可以随时将 const 添加到 facet_converter 结构...
-
这是问题中“想要真正的常量”的部分吗?我已经在考虑了,但总是有“偷偷摸摸”的部分......
-
“可怕的设计分开......” - 这就是问题所在。回到绘图板为你儿子。这个类太复杂了,你的消费者永远不会愿意使用它,你也永远无法维护它。
-
谢谢理查德,但正如我所说,这是我想要解决的问题,并且“你的方法是错误的”并不适用 - 将其视为一种练习。不要担心我的消费者,因为没有(这永远不会最终出现在要使用的代码库中)。在最后一段中,我之前使用了其他两种方法,但这是我现在想尝试的一种,看起来很复杂。同样,如果你愿意,可以把它当作一个练习(git 分支称为“实验”)。尽管如此,还是欢迎命名任何其他方法,因为我最终一定会尝试它们。谢谢。
-
@RichardHodges,我创建了以下聊天室chat.stackoverflow.com/rooms/85492/…,以防您想进来讨论替代方法。您的个人资料中提到了 30 多年的游戏制作经验,也许您可以在那里给我一些提示。谢谢。
标签: c++ casting polymorphism