我想要一个具有公共只读访问权限并且可以在私有上下文中编辑的类变量。 在 this 之前的 Stackoverflow Question 中,描述了使用 const 引用。 然而对我来说这会导致奇怪的行为。
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;
}
该程序输出:
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 返回,没有错误或警告。
然后重写 Triangle3 类以不使用 const 引用:
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()) {}
};
这将返回正确的结果:
Triangle 0: (0, 0, 0), (0, 0, 0), (0, 0, 0)
Triangle 0: (0, 0, 0), (0, 0, 0), (0, 0, 0)
有人知道这是什么原因造成的吗? 我严重怀疑 const 引用没有像宣传的那样工作,但为什么呢?
这里的问题是,当您
push_back
到vector
时,您正在复制Triangle
。发生这种情况时,引用仍然引用原始 Triangle
的元素,然后该元素会被销毁,然后您将引用无效元素。这是未定义的行为。每当 vector
需要调整大小时,也会(再次)发生这种情况。
添加复制构造函数将解决该问题:
Triangle3( const Triangle3& o ) : _a(o.a), _b(o.b), _c(o.c){}
但是,这仍然是一个相当危险的做法,而且你还需要
operator=
。