继承 概念原理

继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定
    (子类优先级大于父类的优先级)
    义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。
    // B中的fun和A中的fun不是构成重载,因为不是在同一作用域
    // B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。

    派生类的默认成员函数
    6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个
    成员函数是如何生成的呢?
  5. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  6. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  7. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  8. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  9. 派生类对象初始化先调用基类构造再调派生类构造。
  10. 派生类对象析构清理先调用派生类析构再调基类的析构。
  11. 在这里插入图片描述在这里插入图片描述
  12. 继承与静态成员
  13. 基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。(该成员只能在类外进行定义和声明)
  14. 复杂的菱形继承及菱形虚拟继承
    在这里插入图片描述
    在这里插入图片描述
    菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant
    的对象中Person成员会有两份。
    虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
    在这里插入图片描述
    下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A
    同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指
    针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
    在这里插入图片描述
    在这里插入图片描述
    //代码原理
    //继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特
    //性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,
    //体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

//继承就是主要的是由于访问的权限,将其分为私有,保护,公有三种情况。 测试就是使用的是
//私有继承,父类当中的所有成员,除过父类的private成员不可见,其他成员的访问权限
//保护继承是不能在类外进行访问的。除过父类的private成员不可见,public会变成protected。
//父类成员在子类中的访问权限;min((原有权限,继承方式))
//父类的私有成员在子类中始终不可见。

//#include
//using namespace std;
//class Person
//{
//public:
// void Print()
// {
// cout << “name:”<<_name << endl;
// cout << “age:” << _age << endl;
// }
//protected:
// string _name = “peter”; // 姓名
// int _age = 18; // 年龄
//private:
// int _id = 10;
//};
//class A//空类 一个字节大小
//{};
class student :Person//子类:父类 默认继承是私有private
// class student :public://Person 公有继承
//{
//public:
// void printstu()
// {
// cout << _age <<_name << “student” << endl;
//
// }
//private:
// int _stiid = 20;
// };
//void test()
//{
// cout << sizeof(Person) << endl;
// cout << sizeof(A) << endl;
// cout << sizeof(student) << endl;
//
// student stu;
// stu._age = 10;
// stu._name = “qwf”;
//}
//
//int main()
//{
// test();
// return 0;
//
//}
子类和父类的赋值关系。
切片;子类对象,引用,指针赋值给父类对应的对象,引用,指针。
父类->子类
1.父类对象不能赋值子类对象,也不能进行类型的强制转换
s=p;
2.父类引用,指针不能直接赋给子类的引用,指针,但是可以通过强制类型转换赋值。
不建议强制类型转换,因为可能会产生越界的问题。
student& rp=(student&)rs;
student* pp=(student*)&p;
//
//int a = 2;
//float b = 3.2;
//a = b;//隐式转换

继承中的作用域;
//class A
//{
//public:
// void fun()
// {
// cout << “A::fun()” << endl;
// }
//private:
// int _a = 1;
//};
//class B:public A
//{
//public:
// void fun()
// {
// cout << “B::fun()” << endl;
// }
// void printB()
// {
// //同名隐藏的父类成员;可以通过加上父类的作用域访问。
// //同名隐藏的父类成员;可以通过加上父类的作用域访问隐藏的函数也是一样,就是表示的是同名隐藏但是尽量不要这样去使用。
// //同名隐藏;无论是成员变量还是成员函数,都是成员函数。
// cout << _a << endl;
// }
//
//private:
// int _a = 100;
//};
//void test()
//{
// B b;
// b.printB();
//}
//
//int main()
//{
//
// test();
// return 0;
//}
//
//
//#include
//#include
//using namespace std;
在子类的初始化
//class person
//{
//public:
// person(int id ,int age)
// :_id(id)
// , _age(age)
// {
// cout<<“person(int,int)<<endl”;
// }
// person(const person& p)
// :_id(p._id)
// ,_age(p._age)
// {
// cout << “person(const person&)” << endl;
// }
// person& operator=(const person& p)
// {
// if (this != &p)
// {
// _id = p._id;
// _age = p._age;
// }
// cout << “person& operator=(const person& p)” << endl;
// return this;
// }
// ~person()
// {
// cout << “~person()” << endl;
// }
//private:
// int _id;
// int _age;
//};
//
//class student :public person
//{
//public:
// //父类的成员需要调用父类构造完成初始化。
// //如果父类有默认构造,初始化列表中可以不用显示调用父类的构造函数。
// //如果父类没有默认构造,必须显式调用父类的一个构造函数。
// student(int id, int age, int stuid)
// /
:_id(id)
// , _age(age)/
// :person(id,age)
// , _stuid(stuid)
// {
// cout << “student(int id, int age, int stuid)” << endl;
// }
// //拷贝构造的初始化类表中可以调用普通的父类的构造
// student(const student& stu)
// /
:_id(stu._id)
// ,_age(stu._age)/
// :person(stu)
// , _stuid(stu._stuid)
// {
//
// }
//
// //赋值运算符
// //
// student& operator=(const student& stu)
// {
// if (this != &stu)
// {
// /
_id = stu._id;
// _age = stu._age;*/
// //父类的赋值和子类的赋值运算符构成同名隐藏,需要加上父类作用域
// //
// person::operator=(stu);
// _stuid = stu._stuid;
// }
// cout << “student & operator=(const student& stu)” << endl;
// return *this;
// }
// ~student()
// {
// //析构函数底层的函数名;destructor
// //子类析构和父类析构是同名隐藏。
// //编译器在任何情况下都会自动调用父类的析构,不需要显示调用父类析构,因为如果写了,可能会造成多次析构。
// //~person();
// person::~person();
// cout << “~student()” << endl;
// }
//private:
// int _stuid;
//};
成员的初始化顺序,一定是先初始化父类的成员,然后是子类成员
构造时候显示父类,再是子类的的构造。
但是析构的时候,先析构子类,再去析构父类、
//void test()
//{
// 子类的默认构造会自动调动父类的构造函数
// //student stu(1, 2, 3);
// 子类的默认拷贝构造自动调用父类的拷贝构造
// 如果子类显式定义了拷贝构造,子类的拷贝构造会自动调用父类的默认构造。
// //student copy(stu);
// 赋值 子类的默认赋值运算符也会自动调用父类的赋值运算符
// 子类定义的赋值运算符不会去默认的调用父类的赋值运算符。
// //stu = copy;
//
// //析构的表示
// student stu(1, 2, 3);
//}
//int main()
//{
// test();
// return 0;
//}
//
//
基类的static成员在整个的继承体系中,被所有的对象共享
//class A
//{
//public:
// A()
// {
// ++_a;
//
// }
//private:
// static int _a;
//};
//int A::_a = 0;
//class B :public A
//{};
//class C :public B
//{};
多继承:继承方式 类名,继承方式,类名
//void test()
//{
// A a;
// B b;
//}
菱形继承的问题;数据冗余,二义性 通过菱形虚拟继承来解决问题。

#include
#include<stdio.h>
using namespace std;

class Person
{
public:
void Print()
{
cout<< _name <<endl;
cout<< _age <<endl;
}
protected:
string _name = “peter”; // 姓名
int _age = 18; //年龄
};
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和
//Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可以看到变量的复用。
//调用Print可以看到成员函数的复用。
class Student : public Person
{
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
void test()
{
Student s;
Teacher t;
s.Print();
t.Print();

}

int main()
{
test();
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值