子类中的成员是对父成员的引用

Member in child class a reference to parent member?

本文关键字:父成员 引用 成员 子类      更新时间:2023-10-16

假设我有以下测试代码:

#include <iostream>
using namespace std;
class Vector3 {
public:
    float data[3];
};
class Weird3 : public Vector3 {
public:
    union {
        struct { float &x, &y, &z; };
        struct { float &r, &g, &b; };
    };
    Weird3() : x(Vector3::data[0]), y(Vector3::data[1]), z(Vector3::data[2]) {}
};
int main(int argc, char** argv) {
    const Weird3 w;
    w.x = 100; // Works ok!
    cout << w.x << endl;
    w.data[0] = 100; // Error: assignment of read-only location
    cout << w.x << endl;
    return 0;
}

为什么通过子类中的引用成员修改data成员有效,而不是直接修改?还有,是否有更好的方法来实现这种行为?例如,我有一个模板类VectorN,和衍生类Vector2, Vector3Vector4;我想修改VectorN中的data成员,使用x, y, zr, g, b等子类中的成员。

为什么可以通过子类中的引用成员修改数据成员,而不是直接修改?

因为你没有修改引用。c++中的const不是逻辑const,它更像是位const。当你这样做的时候:

w.x = 100;

你实际上没有修改w.x,所以这是允许的。x恰好引用了const对象的不同数据成员,这意味着这是未定义的行为。但这是完全合法的法典。你不应该这么做。

还有,有没有更好的方法来实现这个行为?

如果你想修改const对象上的成员,就把它们设为mutable

w声明为constconst不适用于被引用的成员(如果是指针,则是指针)。

对于引用来说,这一点就不那么清楚了,因为float& const是不允许的,因为你不能重新分配引用(所以你可以认为它是默认的& const)。

但是有了指针就更容易理解了:

const float *x

这是指向常量float的指针。不能通过解引用的方式给指向的对象赋新值。

float* const y

这是指向float的常量指针。可以通过解引用给指针赋一个新值,但不能给指针赋一个新值(让它指向别的东西)。

现在声明const Weird3 w使所有字段在w中不可重新分配(前面示例中的float* const),但是允许修改它们的值。这意味着你实际上并没有改变w的状态,而是改变了w引用或指向的东西。

为什么可以通过子类中的引用成员修改数据成员,而不是直接修改?

为什么它不能直接工作是很明显的,你试图修改const对象。它允许你修改引用的原因也很明显——你不是在修改对象的状态,而是在修改引用指向的其他东西。引用指向const对象本身或其他对象的事实在这里是不可见的(对于编译器来说),它应该只是防止将const对象赋值给左值引用。

那么问题是为什么你能够将const对象的成员赋值给左值引用。答案是——对象的常量在构造函数中是不可见的。它可能会在未来的语言变化中得到改进,但它现在是这样工作的。

无论如何修改const对象导致UB,所以你的评论"工作正常"是一个错觉。

还有,有没有更好的方法来实现这个行为?

"更好"的方法是直接使用const_cast<>。它至少是显式的,并且可以用来移除原本是非const的对象的const,但是通过修改获得了它。同样,您不应该通过"hack"修改const对象,也不应该使用C强制转换或const_cast<>,因为它会导致UB。