指针"如何获知"指向对象的哪个部分可以访问?

mar*_*zzz 1 c++ polymorphism inheritance pointers

基本示例:

class Base
{
public:
    double mValue = 10.0;
};

class Derived: public Base
{
public:
    double mAdditionalValue = 20.0;
};

int main()
{
    Derived derived;
    Base *rBase = &derived;
    std::cout << "value " << rBase->mValue << '\n';
    std::cout << "additionalValue " << rBase->mAdditionalValue << '\n';
}
Run Code Online (Sandbox Code Playgroud)

显然我无法访问mAdditionalValuevia,rBase因为引用类型(Base)不是Derived,并且不包含被调用的成员mAdditionalValue.太好了!智能机制!但实际上这是用C++实现的?

我的意思是:rBase无论如何指向一个类型的对象Derived,并在内存中存储它mAdditionalValue(我已创建它).所以它指向整个对象.它可以"物理"访问该数据.

谁"阻止/限制"指针范围的可访问性?它在指针的逻辑内(所以是软件实现)还是编译器在编译阶段,捕获它并阻止操作?那会怎么做?检查内存地址并限制它们?

Yak*_*ont 5

我要告诉你一个我们告诉孩子谎言.就像我们告诉他们为什么天空是蓝色的,或者世界是圆的.

所以编译器知道给定指针Base,mValue位于与地址固定的偏移处Base(在上面的例子中,我打赌0).

所以它转化rBase->mValue为"从rBase指向的位置偏移0获得双倍".它通过观察这个静态类型*rBase(在编译时),仰视什么偏移的mValue该类型,并重写代码不再知道rBase的类型,只是一个指针和偏移.

一切都很好.

当你要求rBase->mAdditionalValue它尝试在编译时mAdditionalValue在静态类型中查找偏移量*rBase并且它不存在.所以它会产生错误.

您可以想象一种语言会更进一步说"好吧,mAdditionalValue在派生类型中定义!只需使用该偏移!".

class Derived2: public Base
{
public:
  double bob=0;
  double mAdditionalValue = 20.0;
};
Run Code Online (Sandbox Code Playgroud)

哦哦 现在有两种派生类型,它们不同意关于指向对象mAdditionalValueBase指针的偏移量.所以现在这个非C++编译器必须确定计算*rBase偏移量的运行时类型,这反过来需要开销.

在C++中有一种机制可以为方法(虚函数)而不是数据执行此操作,但即使在那里你也必须;必须在Base中使方法成为虚拟(否则使高效的表变得精神错乱).从理论上讲,C++可以拥有虚拟数据成员.

因此,一般来说,没有一种零成本的方法可以解决您的需求.


对于不同的谎言,你要求的东西违反了封装.你没要求mAdditionalValueBase*.即使很容易,C++应该说不.如果我们盲目跟随的指针错误,那么运行时行为是不好的.它应该是明确的或不可能的.


真正的答案是,因为标准是这样说的.您必须将方法或数据字段的名称放在Base右侧Base->.其他任何事情都是形成不良的计划.


现在您可以编译代码了.

std::cout << "additionalValue " << static_cast<Derived*>(rBase)->mAdditionalValue << '\n';
Run Code Online (Sandbox Code Playgroud)

在这里我们告诉它我们知道它是派生的,一切都运行0运行时成本(如果我们是对的)和未定义的行为,如果我们错了.


这是一个可能的内存布局:

....
0x1110FFFF
0x11110000 DERIVED  BASE  mValue
0x11110001 DERIVED  BASE  mValue
0x11110002 DERIVED  BASE  mValue
0x11110003 DERIVED  BASE  mValue
0x11110004 DERIVED  BASE  mValue
0x11110005 DERIVED  BASE  mValue
0x11110006 DERIVED  BASE  mValue
0x11110007 DERIVED  BASE  mValue
0x11110008 DERIVED  mAnotherValue
0x11110009 DERIVED  mAnotherValue
0x1111000A DERIVED  mAnotherValue
0x1111000B DERIVED  mAnotherValue
0x1111000C DERIVED  mAnotherValue
0x1111000D DERIVED  mAnotherValue
0x1111000E DERIVED  mAnotherValue
0x1111000F DERIVED  mAnotherValue
0x11110010 
...
Run Code Online (Sandbox Code Playgroud)

在指针0x11110000可以在被人指指点点DERIVED,BASE或者mValue,甚至只是在字节0x11110000-他们有一个以上的意义相同的地址.但是,给定指针仅指向其中一个指针,由指针类型决定.

Base*带有值的指针0x11110000指向子BASE对象DERIVED.

  • @markzzz - 不,`rBase`指向`Base`.总是.那个`Base`实际上可能是`Derived`的子对象,但它仍然是`Base`. (2认同)