【问题标题】:Abstract class and unique pointer抽象类和唯一指针
【发布时间】:2017-11-08 20:32:26
【问题描述】:

我的代码中有以下错误:

error: allocating an object of abstract class type 'Material'

我不知道如何处理这种情况。

我知道std::make_unique 执行分配,所以它无法分配Material 类型的对象,但我不知道如何更正它。

#include <iostream>
#include <memory>

struct Material
{
  Material() = default;
  virtual int get_color() const = 0;
};

struct Basic : public Material
{
  Basic() = default;
  virtual int get_color() const override
  {
    return 1;
  }
};

struct Mix : public Material
{
  Mix(const Material& mat1, const Material& mat2)
    : mat1_(std::make_unique<Material>(mat1))
    , mat2_(std::make_unique<Material>(mat2))
  {}

  virtual int get_color() const override
  {
    return mat1_->get_color() + mat2_->get_color();
  }     
private:
  std::unique_ptr<Material> mat1_;
  std::unique_ptr<Material> mat2_;
};

int main()
{
  auto mix = Mix(Basic(), Basic());
  std::cout << mix.get_color() << '\n';
}

【问题讨论】:

  • Mix 的构造函数中,您想为两个对象(unique_ptrs 指向)动态分配存储空间并在那里复制构造函数的参数是否正确?

标签: c++ inheritance constructor c++14 abstract-class


【解决方案1】:

这个电话:

std::make_unique<Material>(mat1)

尝试创建类Material 的实例,这与mat1 的类型无关。你的课堂似乎需要clone() 方法:

class Material {
...
    virtual std::unique_ptr<Material> clone() const = 0;
};

那么Mix ctor 将是:

Mix(const Material& mat1, const Material& mat2)
    : mat1_(mat1.clone())
    , mat2_(mat2.clone())
  {}

你需要在每个派生类中实现clone()

struct Basic : public Material
{
  Basic() = default;

  virtual std::unique_ptr<Material> clone() const override
  {
      return std::make_unique<Basic>( *this ); 
  }

  virtual int get_color() const override
  {
    return 1;
  }
};

【讨论】:

    【解决方案2】:

    问题是因为Mix试图创建抽象类Material的对象:

    : mat1_(std::make_unique<Material>(mat1))
    

    理想情况下,基于签名

    Mix(const Material& mat1, const Material& mat2)
    

    Mix 应该能够对传递给它的任何类型的Material 进行操作。

    Mix 通过抽象类引用传递的事实很好。但是Mix 试图创建派生类的对象这一事实是不寻常的。如果还有其他派生类呢?

    我的设计会稍有不同,让Mix 不是成分的所有者;它们是由外部的东西创建和拥有的,Mix 只是混合了传递给它的东西。

    struct Mix : public Material
    {
      Mix(const Material& mat1, const Material& mat2)
        : mat1_{mat1}, mat2_{mat2}
      {}
    
      virtual int get_color() const override
      {
        return mat1_.get_color() + mat2_.get_color();
      }     
    private:
      Material const& mat1_;
      Material const& mat2_;
    };
    
    int main()
    {
      std::unique_ptr<Material> mat1 = std::make_unique<Basic>();
      std::unique_ptr<Material> mat2 = std::make_unique<Basic>();
    
      auto mix = Mix(*(mat1.get()), *(mat2.get()));
      std::cout << mix.get_color() << '\n';
    }
    

    【讨论】:

    • 这可能是合理的,但它要求调用者必须确保两个 Material 对象的生命周期超过 Mix 对象的生命周期。编译器和其他工具往往无法帮助检查此类需求。这样的要求在维护时可能会出现问题,除非例如单个对象负责维持这种协调。如果其他对象必须控制生命周期,有时这些要求是必要的。有时 Mix 和 MixMaterialHolder 的类型会太多,给 Mix 添加所有权责任会更好。
    【解决方案3】:

    您可以使用模板化构造函数来安排构造正确的类型,而无需克隆方法:

    #include <iostream>
    #include <memory>
    
    struct Material {
        Material() = default;
        virtual int get_color() const = 0;
    };
    
    struct Basic : Material {
        Basic() = default;
        int get_color() const override {
            return 1;
        }
    };
    
    struct Mix : Material {
        template<typename M1, typename M2>
        Mix(const M1& mat1, const M2& mat2)
            : mat1_{std::make_unique<M1>(std::move(mat1))}
            , mat2_{std::make_unique<M2>(std::move(mat2))}
        {} 
    
        int get_color() const override {
            return mat1_->get_color() + mat2_->get_color();
        }
    
    private:
        std::unique_ptr<Material> mat1_;
        std::unique_ptr<Material> mat2_;
    };
    
    int main() {
        auto mix = Mix(Basic(), Basic());
        std::cout << mix.get_color() << '\n';
    }
    

    请注意,这仅在您发送具有已知类型的材质实例时才有效。

    【讨论】:

      【解决方案4】:

      http://www.cplusplus.com/forum/beginner/236974/ 包含正确的解决方案。 make_shared 应该是特定的类型;然后,您可以毫无问题地将其存储在抽象类型的 unique_ptr 中。

      【讨论】:

        猜你喜欢
        • 2017-07-03
        • 2012-12-03
        • 1970-01-01
        • 2011-09-25
        • 1970-01-01
        • 1970-01-01
        • 2015-12-28
        • 2010-12-02
        • 1970-01-01
        相关资源
        最近更新 更多