首页 > 解决方案 > 访问派生类的方法协变返回,派生自带有 unique_ptr 的工厂构造

问题描述

编辑:我的问题可能只是问如何降低 aunique_ptr<base>unique_ptr<derived>(已经回答),但我不是 100% 确定我在问什么

我有一个抽象基类Base

class Base{
  public:
    Base();
    struct pStruct{};
    virtual pStruct pFunc(std::vector<double> data) = 0;
  protected:
    CustomType dataValue;
};

和两个派生类Derived1Derived2实现Base

class Derived1 : public Base {
  public:
    struct pStructD1 : Base::pStruct {
      CustomType data1;
      std::vector<double> data2;
    };
    Derived1(uint32_t foo1, std::vector<double> foo2, ...);
    virtual pStruct pFunc(std::vector<double> data) override;
  private:
    uint32_t bar1{0};
};
class Derived2 : public Base {
  public:
    struct pStructD2 : Base::pStruct {
      int32_t data3;
      std::vector<double> data4;
      double data5
    };
    Derived2(std::vector<double> foo1, std::vector<double> foo2, ...);
    virtual pStruct pFunc(std::vector<double> data) override;
  private:
    std::vector<double> bar2;
};

调用类方法pFunc(std::vector<double> data)时,每个派生类将返回不同的类型和数量的值。我尝试使用协变返回类型进行这项工作,因此Derived1::predict(data).key1可能是矩阵,也.key2可能是其他东西,依此类推。Derived2::predict(data).key1可能是唯一的键,它可能是一个布尔值。每个派生类都定义了自己的::predict()返回字段,因为它们差异很大。

问题是,我用一个工厂构造这些派生类,它读取一些输入(通过 ifstream 构造),并找出它应该是什么派生类,然后调用相应的工厂。

class BaseFactory {
  public:
    static std::unique_ptr<Base> createObj(std::ifstream & file){
      file.read((char *) specificTypeString, 2);//This isn't actually the code, just assume this part works
      if(specificTypeString == "D2"){
        return D2BaseFactory::createObj(file);
      }
      else if(specificTypeString == "D1"){
        return D1BaseFactory::createObj(file);
      }
      else{
        throw std::runtime_error("error");
      }
    }
};

对于“D2BaseFactory”std::unique_ptr<Base> D1BaseFactory::createObj(std::ifstream & file);返回和相同的东西。std::unique_ptr<Derived1>(new Derived1(param1, param2, ...));

问题是,如果我用 common 构造一个 Derived 类BaseFactory,并调用pFunc()返回的 unique_ptr,它总是为空的Base::pStruct == {},因此无法访问协变 pStructs 的成员。我知道这是因为工厂 createObj 返回基本类型,但是有没有办法动态返回我想要的类型,以便我可以访问派生 pStructs 中的必要字段?我认为使用原始指针可能会起作用,但如果可能的话,我想将它们保留为唯一指针。

标签: c++pointersabstract-classfactorycovariance

解决方案


你可以做类似这样的事情:

class Base
{
public:
    ...
    std::unique<pStruct> pFunc(...) { return DopFunc(); } 
protected:
    virtual std::unique<pStruct> DopFunc() = 0;
};

class Derived1 : public Base 
{
public:
    struct pStructD1 : Base::pStruct { ... };

    // Used when calling the child factory directly...
    std::unique_ptr<pStructD1> pFunc(...) 
    { 
        return std::make_unique<pStructD1>(...);
    }

protected:
    // Used when called through the Base factory...
    std::unique<pStruct> DopFunc(...) override
    {
        // Call the other function for code sharing... (DRY)
        return pFunc(...);
    }
    ....
};

如果您在需要派生对象时使用派生类,这可能是有意义的。

真正的问题是为什么你需要使用派生类型......如果它是出于初始化目的,那么也许工厂应该在返回值之前执行它。

如果是为了一些处理,那么也许你应该在pStruct. 这样,您永远不需要知道工厂返回的派生类型。

在某些情况下,访问者模式也可能是一种解决方案。

如果您需要始终使用特定的结构,那么为什么不总是使用特定的工厂呢?

您也可以转换结果,但如果您每次创建对象时都需要这样做,它可能会使代码变得更复杂。

或者,你也可以有一个模板成员函数 let say template <class T> std::unique_ptr<T> pFuncT(...) { ... }。这样,客户端代码可以直接在构造时指定所需的类型。如果类型不正确,可能会引发空对象或异常。


推荐阅读