【问题标题】:c++ factory and casting issuec ++工厂和铸造问题
【发布时间】:2023-04-04 16:02:01
【问题描述】:

我有一个项目,其中有很多相关的 Info 类,我正在考虑通过一个 AbstractInfo 类和一堆派生类来建立一个层次结构,并根据需要覆盖 AbstractInfo 的实现。然而事实证明,在 C++ 中使用 AbstractInfo 类然后创建派生对象之一并不是那么简单。 (见this问题,评论最后一个答案)

我打算创建一个工厂类,它创建一个 Info 对象并始终返回一个 AbstractInfo 对象。我从 C# 中知道,您可以使用接口来做到这一点,但在 C++ 中,情况似乎有些不同。

向下铸造成为一件复杂的事情,而且似乎容易出错。

有人对我的问题有更好的建议吗?

【问题讨论】:

  • 您能详细说明“没那么简单”是什么意思吗?
  • 一般来说,如果您在AbstractInfo 中没有完整的接口,这应该是一个问题,即您需要访问一些仅在子类中定义的方法。但这也可能意味着您的设计不是最理想的。有了这么少的信息,很难说更多。

标签: c++ polymorphism factory casting


【解决方案1】:

您不需要向下转换。看这个例子:

class AbstractInfo
{
public:
    virtual ~AbstractInfo() {}
    virtual void f() = 0;
};

class ConcreteInfo1 : public AbstractInfo
{
public:
    void f()
    {
        cout<<"Info1::f()\n";
    }
};

class ConcreteInfo2 : public AbstractInfo
{
public:
    void f()
    {
        cout<<"Info2::f()\n";
    }
};

AbstractInfo* createInfo(int id)
{
    AbstractInfo* pInfo = NULL;
    switch(id)
    {
    case 1:
        pInfo = new ConcreteInfo1;
        break;

    case 2:
    default:
        pInfo = new ConcreteInfo2;
    }

    return pInfo;
}


int main()
{

    AbstractInfo* pInfo = createInfo(1);
    pInfo->f();
    return 0;
}

【讨论】:

    【解决方案2】:

    不要沮丧 - 使用虚拟方法。只需从工厂返回指向基类的指针,并仅通过该指针工作。

    【讨论】:

    • 我明白了...但是如果您执行 AbstractInfo* info = new DerivedInfo(),您至少需要让编译器知道您需要哪个版本的虚函数,这不被认为是向下转换??
    • 会自动调用最派生类的版本,不需要向下转换。
    • 编译器根本不需要任何帮助来确定使用哪种方法。这就是virtual 所做的。当您在指针上调用虚方法时,编译器知道该值实际上可能是另一个派生类型。它检查对象的类型,然后查找相应的虚方法。
    【解决方案3】:
    class AbstractInfo
    {
      public:
        virtual ~AbstractInfo();
        virtual X f();
       ...
    };
    
    class Info_1 : public AbstractInfo
    {
        ...
    };
    
    class Info_2 : public AbstractInfo
    {
        ...
    };
    
    AbstractInfo* factory(inputs...)
    {
        if (conditions where you would want an Info_1)
            return new Info_1(...);
        else if (condtions for an Info_2)
            return new Info_2(...);
        else
            moan_loudly();
    }
    

    如果您不希望工厂方法在下游客户端代码添加 Info 类型时成为单点维护,您可以改为为客户端代码提供一些机制来注册用于创建这些派生对象的方法。查看 Gang of Four 的设计模式一书以了解创建模式,或在 Google 上搜索它们。

    【讨论】:

      【解决方案4】:

      虽然在 C++ 中通常不能重载返回类型,但 covariant return types 有一个例外

      示例取自维基百科:

       // Classes used as return types:
       class A {
       }
      
       class B : public A {
       }
      
       // Classes demonstrating method overriding:
       class C {
           A* getFoo() {
               return new A();
           }
       }
      
       class D : public C {
           B* getFoo() {
               return new B();
           }
       }
      

      从而消除了铸造的需要。

      【讨论】:

      • extends - 来自什么语言?并且您想返回一个指向新分配对象的指针。那里也没有工厂设施。
      • 我尝试修复示例代码。它现在应该是“C++”。希望你没事,彼得。
      • @sbi 如前所述,它只是取自维基百科的一个示例,用于说明协变返回类型,从未声明过,也不打算成为 C++。但是考虑到这个问题没有花时间去 C++ 化这个例子对我来说确实是一件非常愚蠢的事情。所以谢谢:)
      【解决方案5】:

      C++ 提供 polymorphism 就像 C# 一样。该语言没有特殊的接口类型,但您可以通过使用只有pure virtual methods 的类来模拟它。在 C# 中,所有方法默认都是虚拟的(意味着它们在运行时绑定),而在 C++ 中,您必须使用virtual-关键字显式声明它。此外,C# 使用引用处理所有对象(据我所知),而在 C++ 中,您必须在值、指针或引用之间进行选择。在您的情况下,您很可能希望您的工厂返回指向接口的指针,甚至更好的是 smart pointer,因此您不必担心内存管理。

      【讨论】:

        【解决方案6】:

        为了详细说明/夸夸其谈,使用抽象接口(例如:具有虚函数的基类)的“好”时机是当对象上使用的几乎所有功能都可以包含在虚函数中时。如果是这种情况,您可以轻松地按照您的建议进行操作,只需在基类指针上调用虚函数,这将自动调用提供的最衍生版本。

        如果您发现自己需要经常向下转换以获取子类特定的函数/数据,那么这种方法可能不是您的最佳选择。在这种情况下,您可能会发现自己在类之外编写一些功能,为每种类型提供多个实现,并在必要时使用某种 RTTI 来帮助向下转换。这比较混乱,但往往在“学术”或完全孤立的用法之外更常见。

        不过,您在其他答案中似乎有很多很好的信息/建议。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-12
          • 1970-01-01
          • 2021-01-16
          • 1970-01-01
          • 2011-10-12
          • 1970-01-01
          相关资源
          最近更新 更多