【C++】继承

菱形继承不好理解,建议自己看完代码再回想一遍 

目录

1.继承的理解

2.继承的格式书写

 3.继承关系和访问限定符

4.基类和派生类对象赋值转换

回顾内置类型的隐式类型转化 

​编辑​编辑5.继承中的作用域 

6.派生类的默认成员函数 

7.单继承和多继承

 8.总结


1.继承的理解

继承是类设计定义层次的复用,允许程序员在保持原有类特性的基础上进行扩展,呈现了面向对象
程序设计的层次结构

比如Student类 继承 Person类 ,Person类叫做父类 对应地 Student叫子类,或者,Person类叫做基类 对应地 Student叫派生类

派生这个词很形象

我们可以把派生类看成一种基类的延伸

 派生类的内部有基类成员

2.继承的格式书写

 可以看到,派生类的内部有基类成员

 3.继承关系和访问限定符

访问限定符:public protected private 

 继承方式:public private protected

两个的限定程度 public>protected>private

基类成员访问方式:

 助记:

基类的private肯定类外的都不可见,这是private设计初衷,即使继承也看不见

当继承方式和访问限定符权限不统一时,取权限小的

  • 不写继承方式时,class默认私有继承,struct默认公有继承

4.基类和派生类对象赋值转换

派生对象可以赋值给基类对象,但是基类对象不能赋值给派生对象(向上转化,不允许向下转)

现在子类有个变量想赋值给父类,这个看起来很合理 

这个叫切片,把父类需要的变量切走

 但是父类的变量赋值给子类

这样一看就很荒谬,因为父类的成员不可能比子类还多,如果多,那也是私有成员,不能被继承的


回顾内置类型的隐式类型转化 

把a赋值给b,会发生隐式类型转换(具体内容看内含隐式类型转换),但是把a赋值给c会报错

 

 因为a会先产生临时变量,该变量是double类型,然后在把临时变量赋给c这个引用

但是临时变量有常性,赋值给c相当于权限放大(临时变量只读,但是c可读可写)

加上const对c限制只读就行了

可以看到只能子类给父类,不能反向 

 

可以看到 pp和rp都完成了切片,并且没有报错,说明并没有产生有常性的临时变量

因此基类和派生类的赋值转化 中间不存在类型转化,没有产生中间变量

 虽然基类不能赋值给派生类(绝大多数一般情形下),但是基类指针可以通过类型强转赋值给派生类

父类指针强转后赋值给子类,可能会越界

5.继承中的作用域 

 基类和派生类都有独立的作用域

 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
也叫重定义

隐藏只要是同名函数即可,对参数列表没有要求,所以他可以伪装得很像重载

 两个fun构成什么关系:重写,重定义,重载,编译报错

肯定不是重载,重载要求两个函数在同一个作用域,但是基类和派生类有独立的作用域

重写目前不讲,肯定也不是他

那一定就是重定义

第一个函数调用的结果表是什么?打印A,打印B,编译报错

答案是编译报错,因为子类B不能直接访问父类A的同名成员函数,并且B::fun()没有缺省情况

第二个函数调用没有问题

想访问父类的同名成员函数怎么搞?指定作用域显示访问

 建议:实际中在继承体系里面最好不要定义同名的成员,不给自己找麻烦

6.派生类的默认成员函数 

普通类:

构造/析构:内置类型不处理,自定义类型调用自己的析构/构造

拷贝构造/赋值重载:内置类型浅拷贝,自定义类型调这个成员的拷贝/赋值

派生类的成员变量:

0.基类对象——————————>调用过基类的对应函数实现四个功能(构造/析构/拷贝/赋值)

1.派生类自己的内置类型————————————>和普通类保持一致

2.派生类自己的自定义类型———————————>和普通类保持一致

特殊地

  • 析构函数

1.子类析构函数和父类的析构函数默认构成重定义,由于多态的关系需求,所有析构函数都会特殊处理成destructor(函数名)

2.子类先析构,父类先构造,派生析构函数中无需显示调用基类析构,派生类析构之后自动调用父类析构

很好理解,构造肯定先有基类,析构肯定不能一上来先把基类弄没

class A
{
public:
	void fun()
	{
		cout << "A" << endl;
	}
	A()
	{
		cout << "A()" << endl;

	}
	~A()
	{
		cout << "~A()" << endl;
	}
	int _age;
};
class B:public A 
{
public:
	void fun(int i=0)
	{
		cout << "B" << endl;
	}
	B()
	{
		cout << "B()" << endl;
	}
	~B()
	{
		cout << "~B()" << endl;
	}
	int _num;
};
int main()
{
	B b;
	return 0;
}

 

  •  友元关系不能继承,也就是说基类友元函数不能访问子类私有和保护成员

 

  •  静态成员

普通成员:子类和父类不是一份

静态成员:属于整个类的所有对象,同属于派生类及其对象,子类和父类是一份 

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
string _seminarCourse; // 研究科目
};
void TestPerson()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

最后的结果:4 0

三个类构成单继承关系,每次创建一个子类对象都会被调用父类的构造函数,count++

最后只把Student类的count置空整个类的count都改变,说明三个类使用的一个count,因为他是常量在静态区,和之前的知识不矛盾

7.单继承和多继承

多继承中有一种特殊的继承——菱形继承(继承的形状像菱形)

 菱形继承有问题

class Person
{
public:
	string _name; // 姓名
	// id 家庭住址 身份证号码
};

class Student : virtual public Person
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

// 数据冗余/二义性
int main()
{
	Assistant a;
	a._name = "小张";
	a.Student::_name = "张三";
	a.Teacher::_name = "张老师";

	return 0;
}

 在Assistant的对象中,Person成员有两份,可以发现,每次改变name,Person的名字就会变,如果Person里面的成员变量是int id表示身份证,难道小张,张老师,张三会有三个id?(二义性)

class A
{
public:
	int _a;
};
// class B : public A
class B :  public A
{
public:
	int _b;
};
// class C : public A
class C :  public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

 很明显看到代码冗余

 怎么解决菱形继承的坑?虚继承 virtual

虚继承只能在菱形的腰部 

class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A
{
public:
	int _b;
};
// class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

 变成了这样,发现A位置改变了,并且BC里面有一个奇怪的地址,是B和C的两个指针 的地址,指针指向一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量
可以找到下面的A

 为什么要找到BC自己的A?

出现这样的情况,只有通过d找到BC自己的A才能顺利赋值

  • 组合

这个是我们学的继承,特点是:白箱复用,耦合度高:任何一个成员调整都可能影响B

这个是组合,特点:黑箱复用,耦合度低:只有M中的公有函数调整才会影响N 

 8.总结

优先使用对象组合,而不是类继承

多继承可以认为是C++的缺陷之一 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值