目录
继承发生时基类可见性问题
定义Person基类,包含一个成员函数和三个不同权限限制的成员变量; 定义Student类继承该基类,且只有一个私有成员变量。
class Person{
public:
int func() {
return m_private;
}
int m_public = 1;
protected:
int m_protect = 2;
private:
int m_private = 3 ;
};
class Student :public Person{
private:
int m_id = 4;
};
int main() {
Student stu;
return 0;
}
查看 stu对象的分布:
可以看到,stu 子类对象总共包含16个字节的内容,其中12字节是继承于基类的三个成员变量,4个字节是自己类内的一个成员变量。
可以得到第一个结论:基类的可见性(权限)限制不影响子类对基类的彻底继承,可见性只不过是编译器在编译时刻做的限制。
思考一下? 既然只是在编译时刻做的限制,那在程序跑起来的时候(运行时期),是不是可以通过指针去访问基类中 private 修饰的变量 :
从上面的内存分布我们可以看到,stu对象的起始地址为 CB4结尾的地址,也就是从成员变量m_public 开始计算起,那么从该地址开始 + 8个字节 是不是就到了 m_private这个成员变量的地址:
int main() {
Student stu;
int * p = (int *)( (char *)&stu + 8 ); //强行拿到stu对象中m_private变量的地址
*p = 8; //修改m_private 变量的值
}
看看上述代码能否成功:
单步走后,基类中的privte修饰的 m_private 变量真的被修改了。虽然这是一种绕过编译器限制的做法,但是可以通过此实验确定: 访问权限的检查是在编译器在 编译时期做的,而不是在运行时期。
基类子类指针转换安全性问题
还是上个主题的代码,基类为Person ,子类为 Student :
class Person{
public:
int func() {
return m_private;
}
int m_public = 1;
protected:
int m_protect = 2;
private:
int m_private = 3 ;
};
class Student :public Person{
private:
int m_id = 4;
};
先来看看子类指针转换为父类指针的情况:
int main() {
Student stu;
Person *p_ptr = &stu; //子类指针转换为父类指针
return 0;
}
单步走查看一下 p_ptr 的情况:
可以看到进行指针的转换后 ,属于正常指针转换的情况;
当子类指针转换为基类指针时,子类的东西会更多,因此转换为基类的时候对应的空间范围会缩小,这种情况是安全的。 --- (也就是说学生都是人 , Student 都是 Person)
再看看父类指针转换为子类指针的情况:
int main() {
Person per;
Student stu;
Student *s_ptr = (Student *)&per;
return 0;
}
单步走看看内存分布:
原先基类对象 per 成员变量只有三个,也就是占 3* 4 = 12 个字节的空间,但我们进行强转为子类指针后,可以看到多了4个字节空间。
从指针 s_ptr 的内存分布就可以看出来:
这4个字节属于越界访问 ,本来Person 类只占有12个字节的空间,但是进行 基类指针强制转换为子类指针后, 会造成后面四个字节的空间也是属于基类的。(要注意,子类本来有16Byte,基类本来有12Byte)。因此这四个字节是子类的,并不是基类的。
因此 ,基类指针转换为子类指针时 , 基类的东西可能没有子类那么多,如果子类有自己的东西的时候, 基类转换为子类时候对应的空间范围会增大,这是一种越界行为,是不安全的。--- (学生都是人 ,但不能说人都是学生 )
总结
1. 基类的可见性(权限)限制不影响子类对基类的彻底继承,可见性只不过是编译器在编译时刻做的限制。
2. 子类指针转换为基类指针是安全的 ; 基类指针转换为子类指针是不安全的。