Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract class and unique pointer

I have the following error in my code:

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

I don't know how to handle this case.

I'm aware that std::make_unique performs an allocation, so it can't allocate the object of type Material, but I don't know how to correct it.

#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';
}
like image 997
Colt Avatar asked Jun 07 '17 13:06

Colt


People also ask

Can abstract class have pointers?

Abstract classes act as expressions of general concepts from which more specific classes can be derived. You can't create an object of an abstract class type. However, you can use pointers and references to abstract class types.

What does unique pointer do?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

What is a unique pointer in C ++?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.

Do abstract classes have Vtables?

Yes, abstract classes do have vtables, also with pure abstract methods (these can actually be implemented and called), and yes - their constructor does initialize the pure entries to a specified value.


2 Answers

This call:

std::make_unique<Material>(mat1)

tries to create an instance of class Material, it is irrelevant what type mat1 has. You seem to need method clone() in your class:

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

then Mix ctor would be:

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

and you need to implement clone() in every derived class:

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;
  }
};
like image 188
Slava Avatar answered Sep 17 '22 21:09

Slava


You can use a templated constructor to arrange for constructing the right types without needing a clone method:

#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';
}

Note that this works only if you send instance of a material with a known type.

like image 25
Guillaume Racicot Avatar answered Sep 17 '22 21:09

Guillaume Racicot