【问题标题】:reuse factory type constructor in subclass在子类中重用工厂类型构造函数
【发布时间】:2012-12-06 18:54:59
【问题描述】:

对不起,我对 C++ 很陌生,这种事情可能吗?

我有一个超类,它有一个巨大的构造函数,所以我创建了一个静态工厂类型构造函数,它会做一些工作,然后返回 new ParametricShape(blah blah blah...);

class ParametricShape: public ModelView{

public:
    //actually has 15 arguments didn't want to type them all up
    ParametricShape(func x, funcy, funcz, float lu, float hu, float lv, float hv, int sv, int su);

     static ParametricShape* makeDonutShape(float);
 }

稍后我想在扩展类中使用这个静态方法。

class Donut : public ParametricShape{
    Donut();
}

Donut::Donut(){
  this = ParametricShape::makeDonut(1.0f);
}

这就是我正在尝试做的事情,我一直在摸索各种复制构造函数,但又遇到了各种不同的错误。目前它只是说:左值需要作为赋值的左操作数。感谢您的帮助!

【问题讨论】:

  • 为什么你想要那个?这没有意义......?!
  • 您不能修改this 指针。相反,重写“make...”方法:它们不应该返回新实例,而是修改现有实例。另外,我认为您不想在您的情况下覆盖形状类,因为您没有引入任何新成员,而仅提供参数函数的创建例程
  • @KerrekSB 因为我想扩展参数形状以使其始终看起来像一个甜甜圈并在渲染中覆盖其他一些东西等我能做的事情非常有限,因为我得到了一个模型视图类带有我不允许更改的隐私/受保护的东西。

标签: c++ factory copy-constructor


【解决方案1】:

您不能分配给“this”。它是一个 const 类型。

The type of the this pointer for a member function of a class type X, is X* const.

在工厂设计模式中,您通常有一个单独的类来负责创建新实例。这种对象创建与类的分离是这个设计模式的重点。 See this discussion on how to implement this in C++

【讨论】:

    【解决方案2】:

    这是典型的“工厂”模式:

    #include <memory>
    
    struct Shape
    {
        enum Type { Donut, Sphere, Teapot };
    
        static std::unqiue_ptr<Shape> make(Type type);
    
        virtual ~Shape() = default;
    
        // ...
    };
    
    struct Donut : Shape
    {
        // ...
    };
    
    std::unique_ptr<Shape> Shape::make(Shape::Type type)
    {
        switch(type)
        {
            case Donut: return { new Donut; }
            default:    return { };
        }
    }
    

    用法:

    auto p = Shape::make(Shape::Donut);
    

    【讨论】:

      【解决方案3】:

      虽然我同意大多数其他答案,但在您的情况下,您不需要经典的工厂模式。如我所见,您希望有一个类来描述具有几何参数函数的形状。您希望提供方便的方法来为某些特殊情况创建这些形状,例如“甜甜圈”。只要您不想使用任何其他继承功能,例如为特殊形状类型重载某些方法,您可以简单地删除您的其他类,如 Donut,并在您的客户端代码中使用“制造商”功能。

      一个使代码看起来更加面向对象的选项(并保留子类并在客户端代码中使用它们的构造函数),您可以将创建函数重写为初始化函数,就像我想调用它们一样。请注意,我还引入了我们在专用类中需要的默认构造函数:

      class ParametricShape {
          func x, y, z;
          ...
      
      public:
          ParametricShape();
          ParametricShape(func x, func y, func z, ...);
      
      protected:
          void initDonutShape(float);
      };
      
      
      class Donut : public ParametricShape {
      
      public:
          Donut(float radius) :
              ParametricShape()         // call default constructor of ParametricShape
          {
              initDonutShape(radius);   // initialize functions for "Donut"
          }
      };
      

      现在,像这样实现initDonutShape 方法:

      void ParametricShape::initDonutShape(float radius) {
          // set the parametric functions
          x = ...
          y = ...
          z = ...
      }
      

      而不是返回ParametricShape 的新实例。

      【讨论】:

      • 覆盖一些渲染的东西正是我在这里想要做的,我希望它总是以某种方式翻译并具有某种纹理。不幸的是,这是一个学校项目,我已经获得了一个我无法更改的模型视图类,他们将其中的一些东西设为受保护/私有。关于初始化函数的好建议我将走那条路。干杯!
      • @schwiz 最后,这个方法和你的非常相似,但最重要的改变是:在子类的构造函数中,ParametricShape 实例已经创建(这是因为继承;你无法避免这种情况)。你应该只修改它,而不是扔掉它并重新创建另一个。然而,如果你想以不同的方式对待它们,你只需要每个特殊形状类型的一个子类(例如,每种类型都有一个重载的渲染方法)。
      • 最后一点:您也可以在 Donut 构造函数中初始化参数函数。您根本不需要 init 函数。但是,如果您想在不同类型之间重复使用它们,例如从纹理中分离几何形状并创建木制甜甜圈 + 金属甜甜圈,它们会很好。但在这种情况下,请给 init 方法一个有意义的名称,例如“initDonutGeometry”、“initWoodTexture”等
      • 是的,我拥有的“make”函数每个都有大约 30 多行代码,所以这就是我避免再次使用构造函数的原因。我应该刚开始就走 init 路线。吸取教训!
      【解决方案4】:

      是的,有可能... :)

      #include <new>
      Donut::Donut() {
          this->~Donut();
          new (this) ( *((Donut*) ParametricShape::makeDonut(1.0f)) );
      }
      

      确保定义了复制构造函数, this-&gt;~Donut(); 确保通过默认构造函数分配的所有内容都被删除,第二行 new (this) ( (Donut*) ParametricShape::makeDonut(1.0f) ); 首先创建一个甜甜圈对象,将其重新解释为甜甜圈[注意,这里没有问题,因为甜甜圈没有定义任何变量而不是它的父级]并调用复制构造函数...

      new (this) 不会从内存中分配任何新的存储空间,它只是调用构造函数:)

      【讨论】:

      • 虽然很有创意,但这个答案并没有太大帮助,因为它会导致非常糟糕的代码设计。我敢肯定你的回答很讽刺。
      • 我很确定你不能在构造对象之前调用析构函数。另外我感觉makeDonut已经调用了构造函数,会导致无限递归。
      • 不,其实你可以调用析构函数。这是 C++ 11 之前构造函数重用的常用模式。另外,根据所问的问题,ParametricShape::makeDonut 不调用 Donut(),这是一个罕见的地方,你不想改变已经设计的代码,但想要添加扩展并为更干净的 API 创建包装器,但内部很脏。这段代码,有效!
      猜你喜欢
      • 2021-02-07
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多