【问题标题】:How can I initialize objects from base class array, containing different derived classes, using a for loop?如何使用 for 循环从包含不同派生类的基类数组初始化对象?
【发布时间】:2020-11-04 22:13:26
【问题描述】:

我真的对 OOP 背后的想法感到惊讶,因为我发现考虑类和对象要容易得多,就好像它们是真实的一样。我有一个基类 Tooth 和不同牙齿组的派生类(它们有自己的细节)。它们的构造函数将绘制的纹理作为参数; 对于上牙,它是这样的:

class Tooth 
{ 
   public:
   Tooth(Texture &texture);
};

class Molar : public Tooth { };
class Premolar : public Tooth { };
class Frontal : public Tooth { };

main()
{
   Molar *molar[6];
   Premolar *premolar[4];
   Frontal *frontal[6];

   Texture uppertextures[8];   //only 8 textures, since the other half of the teeth are mirrored

   Tooth* upperTeeth[16] = { molar[0], molar[1], molar[2], premolar[0], premolar[1], frontal[0], frontal[1], frontal[2], frontal[3], frontal[4], frontal[5], premolar[2], premolar[3], molar[3], molar[4], molar[5] }; // < the order in which the teeth have to be added to the graphic scene;

   int x = 0     //for the position of the tooth on the screen;
   int texture = 0;    //we will need it to assign the textures in order;

   for(int i = 0; i<16; i++)
   {
       upperTeeth[16] = new !?!?!?(uppertextures[i])
       upperTeeth[16]->setPositionX(i+x) // some function for setting the position on the screen from left to right;
       
       if(i<8) texture++;     //setting the textures for the teeth on the left;
       if(i>8) texture--;    //setting the textures for the teeth on the right(the same, but in reverse order);
       
       upperTeeth[16]->setPositionY(y)
}


}

如您所见,问题在于分配。我想以与创建基类数组相同的方式创建 new[],并填写派生类的顺序,其中对象出现。当然,如果你有完全不同的解决方案,你可以分享它,但这是我能想到的最聪明的方法。

【问题讨论】:

  • 您可以首先观察将任何内容分配给upperTeeth[16] 是未定义的行为。所以,你已经处于崩溃领域,就在起跑门外。就学习如何正确构造对象而言,这应该很好地涵盖in every good C++ textbook。你的教科书中有没有关于这个主题的具体内容,你不清楚,并且有疑问?你不会通过在这里提问来学习 C++,而只能通过一本好的教科书来学习。
  • 很难理解你真正想要达到的目标。 upperTeeth[16] = new !?!?!?(uppertextures[i]) 应该如何知道要实例化哪种对象?辨别因素是什么?请注意,您可能会对工厂模式感兴趣
  • 不,你应该做的是get a good C++ textbook, and work your way through it。 C++ 是当今使用的最复杂和最难的通用编程语言,在 stackoverflow.com 上的简短回答中根本不可能完全解释所有内容。 SO 确实不是教科书的替代品,而只是一个特定的 Q/A 网站。
  • 这有点类似于尝试学习如何开车而不左转。技术上可行,但并非真正可取。
  • 编程必须实用,这意味着不要添加不会增加价值的东西。我认为为不同种类的牙齿设置不同的类没有任何价值。如果患者有一些无法分类的异常牙齿怎么办?

标签: c++ for-loop derived-class


【解决方案1】:

很多人会告诉你“买一本教科书”的原因是因为 C++ 中有很多机器在运行。但别担心,没有一个你可以学习很多东西。实践可能是最好的学习方式。

...有几件事需要考虑为您的问题找到解决方案。

首先,您已经设置了构造函数。您有从具有带参数的构造函数的类继承的子类。这不好,它基本上意味着你不能创建你的子类。如果你尝试,你只会得到错误。所以我把所有的构造函数都改成了“默认”,这样它才能真正运行,但是你应该让子构造函数也构建基类,比如

Molar(Texture& texture) 
    : Tooth(texture) 
{
    // ... do whatever here
}

然后是所有权。哪个类“拥有”牙齿数据?你有你的数组,但它们是指针数组,所以它们实际上并不拥有数据。因此,您可以在其他地方拥有数据,以确保它不会在您的函数上下文之外被垃圾收集。 或者只是让数组实际上包含牙齿类。我在我的解决方案中这样做了,因为它更简单。但是您可以看到 upperTeeth 仍然是一个指针数组,因为您可能不想拥有对象的两个副本。

Molar molar[6];
Premolar premolar[4];
Frontal frontal[12];

Texture uppertextures[8];   // Only 8 textures, since the other half of the teeth are mirrored

// The order in which the teeth have to be added to the graphic scene
Tooth *upperTeeth[16] = { &molar[0], &molar[1], &molar[2], &premolar[0], &premolar[1], &frontal[0], &frontal[1]
                          , &frontal[2], &frontal[3], &frontal[4], &frontal[5], &premolar[2], &premolar[3], &molar[3]
                          , &molar[4], &molar[5] };

在此示例中,数组将在离开上下文时被销毁。通过使用指针数组和 new 构造函数,您可以在方法完成后继续使用数组,但在不再需要它时跟踪它并删除它,也就是在 析构函数。更简单的方法是在 ma​​in class 的 Header 中定义与我的示例相同的“所有者数组”。

最后对于底部的逻辑位,我用以下代码替换了您的代码:

   int x = 0;     // For the position of the tooth on the screen;
   int y = 0;     // Why not a y variable as well?
   int texture = 0;    // We will need it to assign the textures in order;

   for(int i = 0; i<16; i++) {
       upperTeeth[i]->setPositionX(i + x); // Some function for setting the position on the screen from left to right;

       if (i < 8) texture++;     // Setting the textures for the teeth on the left;
       if (i > 8) texture--;    // Setting the textures for the teeth on the right(the same, but in reverse order);

       upperTeeth[i]->setTexture(texture); // alternative to your constructor

       upperTeeth[i]->setPositionY(y);
   }

使用[i] 而不是[16] 错过的手表。或者,您可以使用

直接遍历您的数组
 int i = 0;
 for (auto tooth : upperTeeth) {
      tooth->setPositionX(i); // you still need i for your case.
      i++;
 }

希望我能解决一些问题!

【讨论】:

  • 天啊……你说得对!这些是指针数组 0.o 该死的...
【解决方案2】:

我终于做了我想做的事。这个问题纯粹是合乎逻辑的。对于所有说我不应该子类化的人——我已经制作了一个程序版本,其中没有牙齿的派生类,在某些时候我觉得它太局限了。现在我发布了有效的代码:

    class Tooth 
    { 
       public:
       Tooth(Texture &texture);
    };
    
    class Molar : public Tooth { };
    class Premolar : public Tooth { };
    class Frontal : public Tooth { };
    
    main()
    {
       Molar *molar[6];
       Premolar *premolar[4];
       Frontal *frontal[6];
    
           Texture texture[8];   //only 8 textures, since the other half of the teeth are mirrored
         
           int toothindex[16] = { 0, 1, 2, 0, 1, 0, 1, 2, 3, 4, 5, 2, 3, 3, 4, 5 };
        
           int textureindex = 0;    //we will need it to assign the textures in order;
        
           for(int i = 0; i<16; i++)
           {
               if(i < 3 || i>12) 
               molar[toothindex[i]] = new Molar(texture[index]);
            
               if(i==3; i==4; i==11; i==12)
               premolar[toothindex[i]] = new Premolar(texture[textureindex]);
    
              if(i>4 && i<12) frontal[toothindex[i]] = new Frontal(texture[textureindex])
    
           if(i<7) texture++; 
           if(i>7) texture--;  
          }
   }  
       

感谢您告诉我所谓的未定义行为并提出 if 语句。我所要做的就是创建一个整数数组,并用它来初始化 for 循环中的特定类。现在看起来像这样:https://imgur.com/a/fghmb9Y

哦...由于我使用的是指针数组,因此对于任何寻求答案的人,不要忘记在不需要对象时创建某种 for 循环来删除对象:

for (int i = 0; i<6; i++)
{
    if(i<4) delete premolar[i];
    delete molar[i];
    delete frontal[i];
}

【讨论】:

  • 我想补充一下,关于这种风格。如果数组已经满了,你可以使用dynamic_cast 而不是直接知道数组的索引。所以像if (dynamic_cast&lt;premolar*&gt;(upperTeeth[i]) != nullptr) 只有在upperTeeth[i] 的项目实际上可以转换为premolar* 时才会通过。我总是使用这种类型检查。
  • 到目前为止还没有学习过投射,所以我不知道它是什么(由于锁定,我在 3 个月前开始编码 :D)我会检查一下!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-03
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多