【问题标题】:Polymorphism and casting through struct with pointers多态性和通过带有指针的结构进行强制转换
【发布时间】: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


【解决方案1】:

您可以创建Super_baseconst Facet_converter 成员,然后通过构造函数设置它。

class Super_base
{
protected:
    const Facet_converter implementations;

public:
    Super_base( const Facet_converter& implementations )
        : implementations( implementations ) {};
    virtual ~Super_base() {};
    const Facet_converter& convert() const { return implementations; }
};

当你实现派生类时,做:

Derived_numeric::Derived_numeric( ) : Super_base( Facet_converter( this, this, NULL ) )

您还需要为该结构添加一个构造函数,以便可以调用:

struct Facet_converter
{
    Facet_converter( Facet_A* const& a, Facet_B* const& b, Facet_C* const& c )
    {
        facet_a = a;
        facet_b = b;
        facet_c = c;
    }

    Facet_A * facet_a;
    Facet_B * facet_b;
    Facet_C * facet_c;
};

我没有使用实际的指针和子类对此进行测试,因此可能需要一些调整。

【讨论】:

  • 这听起来不错...我从来没有想过在每个实例中保留一个正在运行的转换器实例,但它实际上比在每次循环迭代中创建它更有意义,因为它总是会返回同样的结果。拥有两个访问器方法(一个 const 和一个非 const)可以解决这个问题。明天会看看它。谢谢:)。
  • William,讨论正在聊天室中进行,如上面的 cmets 所示。今天早上我仍然会尝试你的方法。我在使用构造函数初始化列表和强制代码中的大量更改或使用构造函数代码保持松散之间纠结。将报告我的发现(如果有)。
  • 已接受答案。我实现了您的解决方案的一个更松散的版本(因此我可以轻松地进行原型制作),并且在我的真实代码中获得了几乎 100% 的功能。我已经用这种不寻常的方法保存了许多代码行,并且我的编译器仍然提供了相当强大的帮助。我不禁认为这有点偷偷摸摸,但很好:)。
  • 很高兴能为您提供帮助!我在整个程序中使用了类似的方法来传递构造函数值,尽管目的非常不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-30
  • 2015-07-13
  • 1970-01-01
  • 2017-01-23
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多