答案C
这里主要说一下 B 选项,在已有知识结构中。我们仅仅知道父类指针可以指向子类对象。
但是反过来,利用子类指针指向父类对象其实也是可以的。仅仅限于编译成功。但是使用时会有问题。比如下面代码:
#include <iostream>
using namespace std;
class Base {
public:
int a = 2;
void pr() {
cout << "Base::pr" << endl;
}
};
class Derive: public Base {
public:
int b = 3;
void pr() {
cout << "Derive::pr" << endl;
}
};
int main (int argc, char ** argv) {
Base Bp1;
Derive *D1 = (Derive*)(&Bp1); // 这种强制类型转换可以
Bp1.pr();
D1->pr();
cout << "D1->b:" << D1->b << endl;
cout << "D1->a:" << D1->a << endl;
return 0;
}
编译器版本: g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
此时编译成功。如果上面利用 dynamic_cast
进行强制转换,此时会失败。
运行结果:
Base::pr
Derive::pr
D1->b:0
D1->a:2
从结果可以看出。B 选项中实际上子类指针可以指向父类实例的。但是这里可以发现,我们打印 D1->b
变量时,此时变量为 0。然后我们调用 D1->pr()
时,能够打印出子类 pr 函数。我们打印 D1->a
时,也能顺利打印为 2。
下面解释原因:
1)为什么子类能够打印函数 pr()?
因为子类成员函数归类所有,不是归实例所有
2)为什么子类打印 b 成员变量时,为 0。(这里也可能是其他数)?
因为实例本身是父类对象。而成员变量是归实例对象所有。因此这里实际上内存中的成员变量就有 a =2 变量。但是这里又能成功访问 b。是因为编译器在编译子类指针 D1 时,认为内存中有 b 这个变量。所以编译能够成功。但是实际上,这块内存是父类对象的。根本没有 b 这个变量。因此,这里打印 b 时,结果就是随机值。
C. 任何引用都必须指向一个实例
这是因为在 C++ 中,引用是一种别名,它必须在声明时被初始化,并且在其生命周期内一直引用某个有效的对象。引用不能在不指向任何实例的情况下存在。因此,选项 C 描述的是引用的正确行为。
其他选项的陈述存在一些问题:
A. 任何指针都必须指向一个实例 - 这是不正确的,指针可以为空(null)。
B. 子类指针不可以指向父类实例 - 这也是不正确的,子类指针可以指向其父类的实例。
D. 引用所指向的实例不可能无效 - 这是不正确的,引用必须在其生命周期内引用有效的对象,但一旦对象被销毁,引用就变得无效。