I wanted to have a class variable that has public readonly access and can be edited in a private context. In this previous Stackoverflow Question it was described to use const references. However for me this leads to strange behaviour.
class Vec3 {
public:
double x, y, z;
Vec3(double x, double y, double z) : x(x), y(y), z(z) {}
Vec3() : x(0), y(0), z(0) {}
};
std::ostream& operator<<(std::ostream& os, const Vec3& a) {
os << "(" << a.x << ", " << a.y << ", " << a.z << ")";
return os;
}
class Triangle3 {
private:
Vec3 _a, _b, _c;
public:
Triangle3(const Vec3& a, const Vec3& b, const Vec3& c) : _a(a), _b(b), _c(c) {}
Triangle3() : _a(Vec3()), _b(Vec3()), _c(Vec3()) {}
const Vec3& a = _a;
const Vec3& b = _b;
const Vec3& c = _c;
};
std::ostream& operator<<(std::ostream& os, const Triangle3& a) {
os << "(" << a.a.x << ", " << a.a.y << ", " << a.a.z << "), ";
os << "(" << a.b.x << ", " << a.b.y << ", " << a.b.z << "), ";
os << "(" << a.c.x << ", " << a.c.y << ", " << a.c.z << ")";
return os;
}
class Mesh {
public:
std::vector<Triangle3> triangles;
Mesh(std::vector<Triangle3> triangles) : triangles(triangles) {}
Mesh() {}
};
int main (int argc, char** argv) {
std::vector<Triangle3> triangles;
int N = 10;
for (int i = 0; i < N; i++) {
triangles.push_back(Triangle3(Vec3(i, i, i), Vec3(i, i, i), Vec3(i, i, i)));
}
std::cout << "Triangle 0: " << triangles[0] << std::endl;
Mesh mesh(triangles);
std::cout << "Triangle 0: " << mesh.triangles[0] << std::endl;
}
This program outputs:
Triangle 0: (9, 9, 9), (9, 9, 9), (9, 9, 9)
Triangle 0: (4.03359e-316, 4.03364e-316, 4.03364e-316), (9, 9, 9), (9, 9, 9)
valgrind --leak-check=full -s returns with no errors or warnings.
Then rewriting the Triangle3 class to not use const references:
class Triangle3 {
private:
public:
Vec3 a, b, c;
Triangle3(const Vec3& a, const Vec3& b, const Vec3& c) : a(a), b(b), c(c) {}
Triangle3() : a(Vec3()), b(Vec3()), c(Vec3()) {}
};
This returns the correct results:
Triangle 0: (0, 0, 0), (0, 0, 0), (0, 0, 0)
Triangle 0: (0, 0, 0), (0, 0, 0), (0, 0, 0)
Anybody know what is causing this? I heavily suspect the const references are not working as advertised but why?
The issue here is that when you are push_back
ing to the vector
, you are making a copy of the Triangle
. When this happens, the references are still referencing the elements of the original Triangle
, which then gets destroyed, and you are then referencing invalid elements. This is Undefined Behaviour. This also happens (again) whenever the vector
needs to resize.
Adding a copy-constructor will solve the issue:
Triangle3( const Triangle3& o ) : _a(o.a), _b(o.b), _c(o.c){}
However, this is still a rather dangerous approach, and you also need operator=
.