拥有Parent-Children类层次结构,如何在单个“Parent”类型变量中保存各种实际子项?

时间:2014-11-21 23:29:29

标签: c++ oop inheritance

假设我有4个班级:

  1. child1
  2. 的child2
  3. child3
  4. 所有孩子都是班级家长的后代。

    在我的玩具程序中,我必须为每个孩子创建一个单独的变量,然后处理需要处理的内容。但是我希望有一个父类型的变量可以作为一个子进行转换。

    这是我目前的解决方案:

    int main(int argc, char* argv[]) {
        Child1 c1 = Child1();
        Child2 c2 = Child2();
        Child3 c3 = Child3();
    
        switch(arg[1]) {
             case CHILD1:
                 c1.start();
                 break;
             case CHILD2:
                 c2.start();
                 break;
             case CHILD3:
                 c3.start();
                 break;
        }
        return 0;
    }
    

    以下是我希望获得的解决方案类型:

    int main(int argc, char* argv[]) {
        Parent p = Parent();
    
        switch(arg[1]) {
             case CHILD1:
                 (Child1)p.start();
                 break;
             case CHILD2:
                 (Child2)p.start();
                 break;
             case CHILD3:
                 (Child3)p.start();
                 break;
        }
        return 0;
    }
    

    我知道上面的代码不正确。但我认为它恰当地传达了我想要达到的目标。我不想浪费内存来创建以前没用的对象。

5 个答案:

答案 0 :(得分:3)

创意一:在交换机中创建项目

int main(int argc, char* argv[]) {
    switch(arg[1]) {
         case CHILD1:
             {
                 Child1 p;
                 p.start();
                 break;
             }
         case CHILD2:
             {
                 Child2 p;
                 p.start();
                 break;
             }
         case CHILD3:
             {
                 Child3 p;
                 p.start();
                 break;
             }
    }
    return 0;
}

好像很多代码都是复制粘贴的吗?对。如果您的Parent具有虚拟析构函数和虚拟start方法,则可以使用工厂模式,从而最大限度地减少重复。

std::unique_ptr<Parent> child_factory(char*) {
    switch(arg[1]) {
    case CHILD1: return std::make_unique<Child1>();
    case CHILD2: return std::make_unique<Child2>();
    case CHILD3: return std::make_unique<Child3>();
    default: throw std::runtime_error("invalid child type");
    }
}

int main(int argc, char* argv[]) {
    std::unique_ptr<Parent> p = child_factory(arg[1]);
    p->start();
    return 0;
}

答案 1 :(得分:1)

如果这些物品不需要长寿,那么你可以这样做:

switch(arg[1]) {
     case CHILD1:
     {
         Child1 c1;
         c1.start();
         break;
     }
     case CHILD2:
     {
         Child2 c2;
         c2.start();
         break;
     }
     case CHILD3:
     {
         Child3 c3;
         c3.start();
         break;
     }
}

另一方面,如果您需要保留更长时间并且使用Parent类型,那么您需要查看分配给基类型指针的动态分配对象。在这种情况下,您可能希望start方法为virtual。然后你可以这样做:

Parent* p = NULL;

switch(arg[1]) {
     case CHILD1:
         p = new Child1;
         break;
     case CHILD2:
         p = new Child2;
         break;
     case CHILD3:
         p = new Child3;
         break;
}

p.start();

// more stuff

delete p;

当然,这里还有很大的改进空间,例如处理arg[1]与任何情况都不匹配的情况。最佳做法是使用p的智能指针,以便更可靠地处理释放。

答案 2 :(得分:0)

投射语法不是(Child)p.start(),而是((Child)p).start()。前者意味着(Child) (p.start())所以它会抛出start返回的值,而不是P。

无论如何,完全不需要施法,因为所有这些都来自“父母”。阅读虚拟方法。有了它们,您可以执行以下操作:

Parent p = ...

p.start();  // the virtual method 'start' takes care of calling the right code

然而#1:没有免费的午餐。虚拟方法可以确保在您调用“开始”时调用孩子的方法。在父级,但仍然必须选择正确的child1 / child2 / child3,具体取决于arg [1]。

Parent p = chooseTheChild( arg[1] );   // you need to write it..

p.start();  // the virtual method 'start' takes care of calling the right code

然而#2:它不适用于普通父母。虚拟方法适用于指针

Parent* p = chooseTheChild( arg[1] );   // you need to write it..

p->start();  // the virtual method 'start' takes care of calling the right code

现在你可以写下选择&#39;功能..再没有免费午餐,仍然是一个开关。由于需要指针,你必须new孩子:

class Parent
{
    public:
        ~Parent() { }
        virtual void start() = 0;
};

class Child1 : public Parent
{
    public:
        virtual void start() { std::cout << "start1!" << std::endl; }
};

class Child2 : public Parent
{
    public:
        virtual void start() { std::cout << "start2!" << std::endl; }
};

class Child3 : public Parent
{
    public:
        virtual void start() { std::cout << "start3!" << std::endl; }
};

Parent* chooseTheChild( ChildType ttt)
{
  switch(ttt) {
     case CHILD1: return new Child1();
     case CHILD2: return new Child2();
     case CHILD3: return new Child3();
  }
}

.. somewhere ...

Parent* p = chooseTheChild( arg[1] );
p->start();  // the virtual method 'start' takes care of calling the right code
delete p;  // new'ed? delete it when finished!

此代码是草图,并不是100%正确和干净,但应该给你一些东西。例如,显然需要智能指针而不是普通指针等等。

答案 3 :(得分:0)

对不起,但这绝对认为继承是错误的。可以考虑 parent children 之间的关系: child_object < em> parent_object ,这意味着 child_object 也可以用作 parent_object ,即a可以被投放,被称为......一个但反之亦然!

Mooing Duck的第二个例子是好东西。

答案 4 :(得分:0)

显而易见的解决方案就是在需要时创建孩子。如果您愿意,可以使用函数模板来消除重复的代码:

#include <iostream>

struct Child1{
  void start() { std::cout << "Start Child1\n"; }    
};
struct Child2{
  void start() { std::cout << "Start Child2\n"; }    
};
struct Child3{
  void start() { std::cout << "Start Child3\n"; }    
};

template<typename Child>
void start() {
  auto child = Child();
  child.start();
}    

int main(int /*argc*/, char* argv[]) {
  auto type = std::string(argv[1]);
  if (type == "CHILD1")
      start<Child1>();
  else if (type == "CHILD2")
      start<Child2>();
  else if (type == "CHILD3")
      start<Child3>();
}

在这种特殊情况下,如果您不想使用virtual方法,则在一个更复杂的情况下,您可能希望拥有一个返回({智能)指针的工厂{{1在Parent上有一个虚拟的析构函数和启动方法。