4

我正在尝试将子对象传递给方法,但提前不知道对象类型,除了它将从父对象继承。我可以将类型设置为父类型,编译器很高兴,但是当这个过程发生时我会丢失子数据。

class Parent{
public:
Parent(int val);
};

class Child{
public:
Child(double childval, int parentval)
};

Child::Child(double childval, int parentval) : Parent(parentval)

Child childObj = new Child(cval, pval);

int someOtherMethod(Parent pobj);
someOtherMethod(childObj);  // Looses child data but parent data persists. How to keep child data too?

很好,但是如果我将 childObj 传递给 someOtherMethod ,则子数据会丢失,并且只保留父数据。

我研究了虚函数、动态绑定、“设置”,但对我应该采取的方向感到完全困惑。

有人可以让我回到正轨吗?

谢谢。

4

3 回答 3

3

如果 Child 拥有的信息或孩子采取的行动不属于 Parent 抽象,那么您可以通过动态转换或检查类型来确定孩子是谁。然而,正如 Edward Corlew 所说,这有一些缺点。

或者,您可以通过双重调度询问孩子是谁。这可以通过将对象传递给孩子然后孩子选择它的类型来完成。然后访问者可以收集它需要的任何信息。

class Child1;
class Child2;

class Visitor
{
public:
  void DoStuff(Child1 & child1);
  void DoStuff(Child2 & child2);
};

class Parent
{
public:
  void DoStuff(Visitor & visitor);
};

class Child1 : public Parent
{
public:
  void DoStuff(Visitor & visitor)
  {
    visitor.DoStuff(*this);
  }
};

class Child2 : public Parent
{
public:
  void DoStuff(Visitor & visitor)
  {
    visitor.DoStuff(*this);
  }
};

潜在地,访问者本身可以来自并允许不同的操作。这消除了在整个程序中传播类型检查的需要。但是,访问者类本身与派生的子级耦合,并且需要在添加具体子级时进行更新。

于 2013-05-01T02:31:50.363 回答
2

正如 Nemanja Boric 指出的那样,您可以选择使用 Dynamic Cast。但是,在某些编译器(例如 MSVC)上使用动态转换可能代价高昂,它会在评估指针时运行字符串比较。

另一个选择是使用静态投射。静态转换在编译时进行评估,因此使用它们几乎没有成本。但是,您仍然必须有一种方法来识别您的类类型,以便知道将其转换为什么。在您的基类中,您可以有一个名为 getType() 的纯虚方法,它返回一个枚举值或一个无符号整数,表示类的 id。然后,您可以在“someOtherMethod”内部检查传入的类的 id 类型,然后对该类执行强制转换。

例如

在某些文件中定义的是一个带有类型的枚举

enum ClassTypes
{
    ClassType1,
    ClassType2,
    ...
};

附带说明:如果您不更改 pObj 指向的内容,则需要将指针设为 const。它让读者知道您并不打算让任何人更改它,并且它也是一种防御编程的形式,通过确保您不会意外更改它然后尝试对其执行强制转换来对抗您最大的敌人(您自己)。

int someOtherMethod(Parent * const pObj)
{
    if(pObj == NULL)
        return 0;

    switch(pObj->getType())
    {
        case ClassType1:
        {
            Class1 * const pClass = static_cast<Class1*>(pObj);
            //Do stuff to class type 1
            break;
        }
        case ClassType2:
        {
            Class2 * const pClass = static_cast<Class2*>(pObj);
            break;
        }
    };

    return 1;
}

正如您所看到的,尽管这可能会增长得非常快,并且它不能很好地处理重复代码。此外,Static Cast 可能很危险,因为在运行时没有检查您正在投射的内容是否有意义,可能会导致崩溃。

您必须考虑类本身的设计。也许你想在这个函数中执行的一些代码你可以在类本身中执行。因此,您不必知道类型,您可以简单地说:

pObj->someOtherMethod(),其中 someOtherMethod 是一个纯虚函数,所有孩子都必须定义。您可能会再次在多个方法定义中遇到重复代码,在这种情况下,某些设计模式(例如 NVI 模式)可能会派上用场。

于 2013-05-01T01:43:45.127 回答
1

明智地规划您的类,使用多态性和明确定义的接口从子类中获取数据。

但是,如果您需要在 C++ 中进行向下转换,可以使用dynamic_cast<T>

int someOtherMethod(Parent *pobj);
{
    Child1 *c1;
    Child2 *c2; 
    Child3 *c3;
    Child4 *c4;



    if((c1= dynamic_cast<Child1*>(pobj)) != 0)
    {
         /* Do stuff here, pobj is instance of Child1 */
    }
    else if((c2 = dynamic_cast<Child2*>(pobj)) != 0))
    {
       /* Do stuff here, pobj is instance of Child2 */ 
    }

    // ...
}

将您的参数作为指向该方法的指针传递,并且您的类中至少需要一个虚函数才能使其工作。

于 2013-05-01T00:48:34.303 回答