上篇C++57个入门知识点_28 继承的可见性(父类成员在子类中可见性;影响因素:父类内部关键字+继承位置关键字;父亲某属性成员,变为儿子中某种属性成员;编译期检查访问权限,子类对象内存包含父类所有成员)中介绍了继承的可见性即父类成员在子类中可见性。本篇讨论可见性究竟是在什么时候发生的呢?
总结:
1. 内存角度看继承及通过指针修改内存
- 子类继承时包含(复制)了父类所有的成员变量,但是由于继承的可见性限制,只可以对部分进行访问
- 继承的可见性是在编译时刻做的限制,本质上可以在运行时通过指针修改
2. 子类转父类是安全的,父类转子类是不安全的(造成越界访问)
1. 内存角度看继承及通过指针修改内存
- 子类继承时包含(复制)了父类所有的成员变量,但是由于继承的可见性限制,只可以对部分进行访问
- 继承的可见性是在编译时刻做的限制,本质上可以在运行时通过指针修改
1.1 内存角度看继承
#include <iostream>
class CPerson
{
public:
CPerson() {
}
~CPerson() {
}
public:
int m_nPublic;
protected:
int m_nProtected;
private:
int m_nPrivate;
};
class CStudent : private CPerson
{
public:
CStudent() {
}
~CStudent() {
}
private:
int m_nStuID;
};
int main(int argc, char* argv[])
{
int nPerSize = sizeof(CPerson);
int nStudSize = sizeof(CStudent);
CStudent stu;
return 0;
}
运行结果:CPerson
类中有3个int
类型成员变量,其子类中数据成员变量为1个int
,从结果看总共16个字节,子类本身的4个字节+CPerson
中的12个字节,子类继承了父类中的所有字节
。
创建CStudent对象,查看其内存
从上面的结果可以看到:子类中包含了父类的所有成员,根据上篇所讲由于关键字的控制,父类的成员具有可见性的差异
。
1.2 通过指针在运行时进行内存修改
#include <iostream>
class CPerson
{
public:
CPerson() {
}
~CPerson() {
}
public:
int m_nPublic;
protected:
int m_nProtected;
private:
int m_nPrivate;
};
class CStudent : private CPerson
{
public:
CStudent() {
}
~CStudent() {
}
private:
int m_nStuID;
};
int main(int argc, char* argv[])
{
int nPerSize = sizeof(CPerson);
int nStudSize = sizeof(CStudent);
CStudent stu;
//(char*)&stu将地址强转为char*,+ 8指向m_nPrivate
//(int*)将地址强转为int*,代表了内部存储的数据类型为int型
int* p = (int*)((char*)&stu + 8);
//p指向的m_nPrivate内容修改为123;
*p = 123;
return 0;
}
运行结果:运行期将m_nPrivate
进行修改
2. 指针转换的安全性
子类转父类是安全的,父类转子类是不安全的(造成越界访问)。
- 儿子的空间范围要比父亲的多,因此儿子可以通过指针转换为父亲
CStudent stu;
//子类指针转换为父类指针(安全),可以说学生都是人
CPerson* pPer = &stu;
运行结果:
- 父亲的空间范围要比儿子的小,经过强制转换pStu就可以访问不属于per的内容,属于
越界访问
CPerson per;
//父类指针转换为子类指针(不安全),不能说人都是学生
CStudent* pStu =(CStudent*)&per;
3.学习视频地址:C++57个入门知识点_29 从内存角度看继承
4.学习笔记:
#include <iostream>
//面向对象:继承
//继承的可见性在何时做的检查?
//是由编译器在编译时刻做的限制,本质上可以在运行时可以通过指针修改
//继承时父类将所有成员赋给了子类,继承部分指针指向父类的内存
//但是由于可见性问题,只能看到没有做限制的成员
class CPerson
{
public:
CPerson() {
}
~CPerson() {
}
int GetGender() {
return m_nGender;
}
void SetGender(int nGender) {
m_nGender = nGender;
}
//父亲公有的都可以访问
public :
int m_nPublic;
//父亲的家族财产给儿子继承
protected:
int m_nProtected;
//父亲的私有财产不能给儿子继承
private:
char m_nszName[255];
int m_nGender;
};
//父类的继承也有三种属性
class CStudent :public CPerson
{
public:
CStudent(){
m_nPublic = 1;
m_nProtected = 1;//类域内部可以访问父类的保护成员
}
~CStudent() {
}
private:
int m_nStuID;
};
//增加老师管理系统,老师的数据与行为和学生是类似的,但也有学号,老师号的不同
//如果重建一个老师类就会造成冗余,易造成错误
class CTeacher :public CPerson
{
public:
CTeacher() {
}
~CTeacher() {
}
private:
int m_nTeaID;
};
int main(int argc,char* argv[])
{
int nPerSize = sizeof(CPerson);
int nStudent = sizeof(CStudent);
CPerson per;
CStudent stu;
//取stu中 m_nszName[255]:&stu取地址,(char*)&stu强制转换为char*
//((char*)&stu + 8) m_nszName前有public和protected8字节
//(int*)((char*)&stu + 8)取地址到p
int* p = (int*)((char*)&stu + 8);
//p指向的内容修改为123;
*p = 123;
//指针转换的安全性
//子类指针转换为父类指针(安全),可以说学生都是人
CPerson* pPer = &stu;
//父类指针转换为子类指针(不安全),但是不能说人都是学生
//CStudent* pStu =(CStudent*)&per;
return 0;
}