Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使此代码正确多态_C++_Class_Polymorphism_Dynamic Cast - Fatal编程技术网

C++ 使此代码正确多态

C++ 使此代码正确多态,c++,class,polymorphism,dynamic-cast,C++,Class,Polymorphism,Dynamic Cast,我有一个抽象类Parent,它有多个子类和空函数,用于与每个子类交互。每个孩子都会覆盖父母的功能,并以不同的方式与其他孩子互动;i、 e.Child1有不同的实现,如interact_with Child1、interact_with Child2等 在Parent中,我有一个函数interactive_和Parent foo。每个希望与另一个孩子交互的孩子都必须首先通过此函数。到目前为止,一切都很好,但随后我们遇到了一个问题:在处理了一些基本逻辑之后,子级需要知道其参数的特定类型,以便它可以继

我有一个抽象类Parent,它有多个子类和空函数,用于与每个子类交互。每个孩子都会覆盖父母的功能,并以不同的方式与其他孩子互动;i、 e.Child1有不同的实现,如interact_with Child1、interact_with Child2等

在Parent中,我有一个函数interactive_和Parent foo。每个希望与另一个孩子交互的孩子都必须首先通过此函数。到目前为止,一切都很好,但随后我们遇到了一个问题:在处理了一些基本逻辑之后,子级需要知道其参数的特定类型,以便它可以继续调用自己的重写函数。目前,我有以下几点:

Child1* child1 = dynamic_cast<Child1*>(foo);
Child2* child2 = dynamic_cast<Child2*>(foo);
Child3* child3 = dynamic_cast<Child3*>(foo);

if(child1 != nullptr){
    interact_with(child1)
}

else if(child2 != nullptr){
    interact_with(child2)
}

else if(child3 != nullptr){
    interact_with(child3)
}
已经有了。

像你说的那样使用动态演员阵容是糟糕的设计。您应该使函数与virtual交互,如声明中所示

virtual void interact_with(Parent foo);
这将使方法调用使用子类的interact_with实现,而不是父类的实现。然后你就可以用这个替换你写的所有东西

interact_with(foo);
像你说的那样使用动态演员阵容是糟糕的设计。您应该使函数与virtual交互,如声明中所示

virtual void interact_with(Parent foo);
这将使方法调用使用子类的interact_with实现,而不是父类的实现。然后你就可以用这个替换你写的所有东西

interact_with(foo);
你想要的是

根据您的需求,您可能会采取许多方法。一个非常通用的方法是使用一个简单的映射形式Keytype,type->Valuefunction,将参数类型对与要调用的函数相关联

在这种情况下,您需要一组void functionParent*类型的自由函数,Parent*为您需要的每个组合提供一个函数,以及一个std::unordered_map类型的映射,其中TypeId是具有值语义的某种形式的类型标识符

然后在运行时执行调度:

if(map_.find(make_pair(type1, type2)) != map_.end())
    map_[make_pair(type1, type2)](obj1, obj2);
在注册每个功能之前:

map_[make_pair(type1, type2)] = func12;
map_[make_pair(type2, type3)] = func23;
....
你想要的是

根据您的需求,您可能会采取许多方法。一个非常通用的方法是使用一个简单的映射形式Keytype,type->Valuefunction,将参数类型对与要调用的函数相关联

在这种情况下,您需要一组void functionParent*类型的自由函数,Parent*为您需要的每个组合提供一个函数,以及一个std::unordered_map类型的映射,其中TypeId是具有值语义的某种形式的类型标识符

然后在运行时执行调度:

if(map_.find(make_pair(type1, type2)) != map_.end())
    map_[make_pair(type1, type2)](obj1, obj2);
在注册每个功能之前:

map_[make_pair(type1, type2)] = func12;
map_[make_pair(type2, type3)] = func23;
....
警告:此解决方案将破坏您的接口。但别担心,它们已经坏了

在我看来,您的设计错误如下:虽然所有子级都是平等的,但您选择其中一个负责交互,并对其调用一个方法。如果您希望数组中3、4、…、N个相等的子级同时交互,那么由哪一个负责

如果在应用程序中,所有对象都同等重要,并且没有对象负责交互,则应将交互移动到自由重载的二进制函数中:

void interact(Child1* a, Child1* b);

void interact(Child1* a, Child2* b);

...

void interact(Child2* a, Child1* b)
{
    interact(b, a); // if order does not matter, reuse another function 
}
显然,它不会解决样板代码的问题,但至少它可以帮助您重新思考您的设计,并找到比双重分派或转换更好的解决方案

此外,根据函数的内部结构,您可能会通过使用模板函数而不是重载函数来轻松减少编写,但不会减少代码大小。

警告:此解决方案将破坏您的接口。但别担心,它们已经坏了

在我看来,您的设计错误如下:虽然所有子级都是平等的,但您选择其中一个负责交互,并对其调用一个方法。如果您希望数组中3、4、…、N个相等的子级同时交互,那么由哪一个负责

如果在应用程序中,所有对象都同等重要,并且没有对象负责交互,则应将交互移动到自由重载的二进制函数中:

void interact(Child1* a, Child1* b);

void interact(Child1* a, Child2* b);

...

void interact(Child2* a, Child1* b)
{
    interact(b, a); // if order does not matter, reuse another function 
}
显然,它不会解决样板代码的问题,但至少它可以帮助您重新思考您的设计,并找到比双重分派或转换更好的解决方案


此外,根据函数的内部结构,通过使用模板函数而不是重载函数,您可能会很容易地减少编写,但不会减少代码大小。

使用双重分派的@imreal答案是正确的。但是,可以使用虚拟成员函数而不是映射和函数指针(实际上与编译器生成的vtable类似)来完成分派

问题是单个虚拟函数无法解决问题,因为您确实需要双重分派,即关于两个对象的虚拟调用,而不仅仅是被调用的对象

请参见以下工作示例:

#include <iostream>

class Child1;
class Child2;

class Parent
{
public:
    virtual void interact_with(Parent* other) = 0;
    virtual void interact_with(Child1* child) {};
    virtual void interact_with(Child2* child) {};
};

class Child1 : public Parent
{
public:
    virtual void interact_with(Parent* other)
    {
        other->interact_with(this);
    }
    virtual void interact_with(Child1* child)
    {
        std::cout << "Child1 - Child1\n";
    }
    virtual void interact_with(Child2* child)
    {
        std::cout << "Child1 - Child2\n";
    }
};

class Child2 : public Parent
{
public:
    virtual void interact_with(Parent* other)
    {
        other->interact_with(this);
    }
    virtual void interact_with(Child1* child)
    {
        std::cout << "Child2 - Child1\n";
    }
    virtual void interact_with(Child2* child)
    {
        std::cout << "Child2 - Child2\n";
    }
};

int main()
{
    Child1 c1;
    Parent* p1 = &c1; // upcast to parent, from p1, we don't know the child type
    Child2 c2;
    Parent* p2 = &c2;

    c1.interact_with(&c2); // single virtual call to Child1 - Child2
    p1->interact_with(&c2); // single virtual call to Child1 - Child2
    p1->interact_with(p2); // double virtual call to Child2 - Child1 (NB: reversed interaction)
}
注意,最后一个是反向的。这是因为使用虚拟函数进行动态调度
在参数中,我必须将this指针与参数交换。如果这些相互作用是对称的,这很好。如果不是,那么我建议在最通用的一个周围创建一个包装器,再次交换this和参数。

使用双重分派的@imreal答案是正确的。但是,可以使用虚拟成员函数而不是映射和函数指针(实际上与编译器生成的vtable类似)来完成分派

问题是单个虚拟函数无法解决问题,因为您确实需要双重分派,即关于两个对象的虚拟调用,而不仅仅是被调用的对象

请参见以下工作示例:

#include <iostream>

class Child1;
class Child2;

class Parent
{
public:
    virtual void interact_with(Parent* other) = 0;
    virtual void interact_with(Child1* child) {};
    virtual void interact_with(Child2* child) {};
};

class Child1 : public Parent
{
public:
    virtual void interact_with(Parent* other)
    {
        other->interact_with(this);
    }
    virtual void interact_with(Child1* child)
    {
        std::cout << "Child1 - Child1\n";
    }
    virtual void interact_with(Child2* child)
    {
        std::cout << "Child1 - Child2\n";
    }
};

class Child2 : public Parent
{
public:
    virtual void interact_with(Parent* other)
    {
        other->interact_with(this);
    }
    virtual void interact_with(Child1* child)
    {
        std::cout << "Child2 - Child1\n";
    }
    virtual void interact_with(Child2* child)
    {
        std::cout << "Child2 - Child2\n";
    }
};

int main()
{
    Child1 c1;
    Parent* p1 = &c1; // upcast to parent, from p1, we don't know the child type
    Child2 c2;
    Parent* p2 = &c2;

    c1.interact_with(&c2); // single virtual call to Child1 - Child2
    p1->interact_with(&c2); // single virtual call to Child1 - Child2
    p1->interact_with(p2); // double virtual call to Child2 - Child1 (NB: reversed interaction)
}

注意,最后一个是反向的。这是因为要对参数使用虚拟函数进行动态分派,我必须将this指针与参数交换。如果这些相互作用是对称的,这很好。如果没有,那么我建议在最通用的方法周围创建一个包装器,再次交换this和参数。

我想虚拟方法是不可能的?请参见以下问题:。严格来说不是重复的,但可能有助于您的Through进程,因为这肯定是您试图解决的访问者模式/双重分派问题。我想虚拟方法是不可能的?请参见以下问题:。严格来说,这不是重复的,但可能有助于您的Through过程,因为这肯定是您试图解决的访客模式/双重分派问题。如果我没有正确解释我自己,我深表歉意。我已经在使用虚拟函数了。在Parent中,我为所有子项提供了与子项交互的虚拟函数。然而,无论哪个孩子与其他孩子互动,都必须先做一些逻辑。在这之后,需要知道什么样的子foo,以便调用正确的interactive_;如果foo真的是一个child4,那没问题,因为我有一个与child4交互的虚拟函数,但在这一点上,我们只知道它是一种父类,而不知道具体是什么样的子类。我不理解你的程序的结构。与父对象交互的对象属于哪些类?是否还有另一个名为interact_with的函数?如果是,它属于哪个类?有父类,也有子类。孩子们以不同的方式与其他孩子互动。Parent包含一个函数interact_with Parent foo,以及每个子类型的interact_with child,这些子类型是虚拟的,并且在每个子类实现中都会发生更改。我们的想法是,每个孩子都以不同的方式与另一个孩子互动,但在这样做之前,无论与哪种类型的孩子互动,都需要做一些逻辑——因此,与父母foo互动。如果我没有正确解释自己,我道歉。我已经在使用虚拟函数了。在Parent中,我为所有子项提供了与子项交互的虚拟函数。然而,无论哪个孩子与其他孩子互动,都必须先做一些逻辑。在这之后,需要知道什么样的子foo,以便调用正确的interactive_;如果foo真的是一个child4,那没问题,因为我有一个与child4交互的虚拟函数,但在这一点上,我们只知道它是一种父类,而不知道具体是什么样的子类。我不理解你的程序的结构。与父对象交互的对象属于哪些类?是否还有另一个名为interact_with的函数?如果是,它属于哪个类?有父类,也有子类。孩子们以不同的方式与其他孩子互动。Parent包含一个函数interact_with Parent foo,以及每个子类型的interact_with child,这些子类型是虚拟的,并且在每个子类实现中都会发生更改。这个想法是,每个孩子都以不同的方式与另一个孩子互动,但在这样做之前,无论与哪种类型的孩子互动,都需要做一些逻辑——因此,与父母foo互动。我今天通过双重分派学到了一些新东西。然而,在我的特殊情况下,这似乎是矫枉过正的,特别是考虑到大量的组合——或者,保留我当前的方法,我所需要做的就是为每个类添加一个cast/else if语句。在这一点上,我也可以像Drop建议的那样做。我今天通过双重分派学到了一些新东西。然而,在我的特殊情况下,这似乎是矫枉过正的,特别是考虑到大量的组合——或者,保留我当前的方法,我所需要做的就是为每个类添加一个cast/else if语句。在这一点上,我不妨做一些像Drop建议的事情。你提出了一些好的观点。即使互动总是由一方发起,一次只能发生一次,我还是喜欢你的方法。然而,我不认为改变我整个项目的结构会有什么好处
这一点,特别是因为,正如你所说,它不会特别解决样板文件的问题。函数内部可能会有很大的不同,顺序也很重要,所以我认为模板的用处也有限。到目前为止,坚持我目前的铸造方法似乎是压力最小、最简单的方法:-你提出了一些好的观点。即使互动总是由一方发起,一次只能发生一次,我还是喜欢你的方法。然而,我不认为在这一点上改变我整个程序的结构会有什么好处,特别是因为,正如你所说,它不会特别解决样板文件的问题。函数内部可能会有很大的不同,顺序也很重要,所以我认为模板的用处也有限。到目前为止,坚持我目前的铸造方法似乎是压力最小、最简单的方法:-这个解决方案非常有效。诚然,我必须为每个类创建相同的虚拟函数,但它摆脱了难看的动态转换,而不会对项目的其余部分产生重大影响。这个解决方案非常有效。当然,我必须为每个类创建相同的虚拟函数,但它摆脱了难看的动态转换,而不会对项目的其余部分产生重大影响。