Reputation: 158
I have a question in the hope that someone here might help me understand that issue. I am currently trying to build a code that needs a container with different types. I have found a formulation that works fine but only after having started out with a slightly different formulation that does not compile. And I do not understand why. Can somebody explain to me why version 1 in the following code does not compile whereas version 2 does?
#include <iostream>
#include <vector>
#include <variant>
struct point
{
double x;
double y;
};
class segment
{
public:
segment()
{
P1.x = 0;
P1.y = 0;
P2.x = 0;
P2.y = 0;
};
virtual ~segment() {};
virtual double get_radius() { return 0; };
virtual double get_length() { return 0; };
virtual double get_angle() { return 0; };
int segment_id = 0;
protected:
point P1;
point P2;
};
class Line : public segment
{
public:
Line() {};
Line(const point pt1, const point pt2)
{
P1.x = pt1.x;
P1.y = pt1.y;
P2.x = pt2.x;
P2.y = pt2.y;
segment_id = 1;
};
~Line() {};
double get_length() { return calc_length(); };
double get_angle() { return calc_angle(); };
private:
double calc_length()
{
// calculate length (here: dummy value)
return 1;
}
double calc_angle()
{
// calculate angle (here: dummy value)
return 0.5;
}
double length = 0;
double angle = 0;
}
;
class circle : public segment
{
public:
circle()
{
center.x = 0;
center.y = 0;
};
circle(const double r, const point c)
{
radius = r;
center.x = c.x;
center.y = c.y;
segment_id = 2;
};
~circle() {};
double get_radius() { return radius; };
point get_center() { return center; };
double get_length() { return 3.14 * radius; }; //returns circumference
private:
double radius = 0;
point center;
};
//-------------------------------------------------------
int main()
{
int nbr = 5;
point start;
start.x = 1;
start.y = 2;
point end;
end.x = 3;
end.y = 4;
point c;
c.x = 0;
c.y = 0;
double r = 9;
circle* myCircle = new circle(r, c);
Line* myLine = new Line(start, end);
//VERSION 1: Does not compile. I get an exception in <memory> line 1762 when trying to delete _Ptr
std::vector<std::unique_ptr<segment>> v1;
v1.emplace_back(myCircle);
v1.emplace_back(myCircle);
std::cout << v1[0]->get_radius() << std::endl;
v1.emplace_back(myLine);
std::cout << v1[1]->segment_id << std::endl;
//VERSION 2: Compiles
std::vector<std::unique_ptr<segment>> v2;
v2.emplace_back(new circle(r, c));
std::cout << v2[0]->get_radius() << std::endl;
v2.emplace_back(new Line(start, end));
std::cout << v2[1]->segment_id << std::endl;
}
Upvotes: 0
Views: 50
Reputation: 409176
The problem is very likely this:
v1.emplace_back(myCircle);
v1.emplace_back(myCircle);
This will create two different std::unique_ptr<segment>>
objects, both using the exact same pointer to the exact same object. This breaks the unique part of the std::unique_ptr
.
When the first element is destructed the second become invalid, and attempting to destruct the second will lead to undefined behavior.
You need to create two different objects that are truly unique:
v1.emplace_back(std::make_unique<circle>(r, c));
v1.emplace_back(std::make_unique<circle>(r, c));
If you want only a single circle
object and that the vector elements should share this object, then std::shared_ptr
might be a good idea. But you still can't create it like you do now, instead you must copy from the first element to the second.
Perhaps something like this:
std::vector<std::shared_ptr<segment>> v1;
v1.emplace_back(std::make_shared<circle>(r, c));
v1.emplace_back(v1[0]); // Create a shared copy of the first circle element
Upvotes: 2