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';
}
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.
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.
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.
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.
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;
}
};
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With