LInux C++学习day08 继承(Inheritance)

一 继承(Inheritance)
1 继承的概念//了解
通过一种机制表达类型之间共性和特性的方式,利用已有的数据类型来定义新的数据类型,这种机制就是继承.
eg:
人类:姓名、年龄、吃饭、睡觉
学生类:姓名、年龄、吃饭、睡觉、学号、学习
教师类:姓名、年龄、吃饭、睡觉、工资、讲课

------------------------------------------
人类:姓名、年龄、吃饭、睡觉
学生类继承人类:学号、学习
教师类继承人类:工资、讲课

		人类(基类/父类)
	  /	  \
 学生类	 教师类(子类/派生类)	
 
 基类--派生-->子类
 子类--继承-->基类

2 继承语法
class 子类:继承方式1 基类1,继承方式2 基类2,…{

};
继承方式:
–>public(公有继承)
–>protected(保护继承)
–>private(私有继承)

3 公有继承的语法特性
1)子类对象会继承基类的属性和行为,通过子类对象可以访问基类中的成员,如同是基类对象在访问它们一样。
注:子类对象中包含基类的部分被称为“基类子对象”

2)向上造型(upcast)//重点掌握
将子类类型指针或引用转换为基类类型指针或引用;这种操作性缩小的类型转换,在编译器看来是安全,可以直接隐式完成。
基类

子类

class A{};
class B:public A{};
class C:public A{};
class D:public A{};
...
void func(A* pa){...}
int main(void){
	A a;
	func(&a);
	B b;
	func(&b);
	C c;
	func(&c);
	D d;
	func(&d);
}

3)向下造型(downcast)
将基类类型的指针或引用转换为子类类型的指针或引用;这种操作性放大的类型转换,在编译器看来是危险的,不能隐式转换,可以显式转换(推荐使用静态类型转换);
基类

子类

4)子类继承基类的成员:公有、保护、私有
–》通过子类,可以直接访问基类中的公有成员和保护成员,就如同它们被声明在子类中一样.
–》但基类中的私有成员,受到访问控制属性的影响,继承后不能直接访问,但是可以让基类提供公有或保护的接口函数来间接访问.
注:基类构造函数和析构函数子类无法继承。

5)子类隐藏基类的成员
–》如果子类和基类中定义了同名的成员函数,因为作用域不同,不能构成重载关系,而是一种隐藏关系,当通过子类访问该成员时,将优先访问子类自己的成员。如果需要通过子类访问基类中被隐藏的成员,可以显式的通过"类名::"来指明。
–》如果形成隐藏关系的成员函数满足不同参的重载条件,也可以通过using声明,将基类中成员函数引入子类作用域,让它们形成有效的重载关系,通过重载匹配来解决//不推荐

4 继承方式和访问控制属性
1)访问控制限定符:影响访问该类的成员的位置
访问控制 访问控制 内部 子类 外部 友元
限定符 属性 访问 访问 访问 访问
public 公有成员 ok ok ok ok
protected 保护成员 ok ok no ok
private 私有成员 ok no no ok
2)继承方式:影响通过子类访问基类中成员的可访问性
基类中的 在公有子 在保护子 在私有子
类中变成 类中变成 类中变成
公有成员 公有成员 保护成员 私有成员
保护成员 保护成员 保护成员 私有成员
私有成员 私有成员 私有成员 私有成员

注:向上造型的语法在私有继承和保护继承中不再适用

class _A{
public:
	void func(void){..}
};
class A:private _A{};

class B:public A{};
class C:public B{};
class D:public C{};
...

5 子类的构造函数
1)如果子类的构造函数没有显式指明基类子对象的初始化方式,那么编译器将会自动调用基类的无参构造函数来初始化基类子对象。
2)如果希望基类子对象以有参的方式被初始化,则必须使用初始化列表。
class 基类{};
class 子类:public 基类{
//使用初始化列表指明基类子对象初始化方式
子类(…):基类(…){}
};
3)子类对象的创建过程
–》分配内存
–》构造基类子对象(按继承表顺序)
–》构造成员子对象(按声明顺序)
–》执行子类构造函数代码

6 子类的析构函数
1)子类的析构函数,无论自己定义的析构函数还是编译器缺省提供的,都会自动调用基类的析构函数,完成基类子对象的销毁。
2)子类对象的销毁过程
–》执行子类析构函数的代码
–》析构成员子对象(按声明逆序)
–》析构基类子对象(按继承表逆序)
–》释放内存

扫描二维码关注公众号,回复: 6202626 查看本文章

3)基类的析构函数不能自动调用子类的析构函数,所以对一个指向子类对象的基类指针使用delete操作符,实际被调用的仅是基类的析构函数,子类的析构函数执行不到,有内存泄漏的风险。
class A{};
class B:public A{};
A* pa = new B;//pa:指向子类对象的基类指针
delete pa;//实际仅执行基类的析构,可能内存泄漏.

解决方法:虚析构函数(后面讲)

7 子类的拷贝构造和拷贝赋值
1)子类的拷贝构造
–》如果子类没有定义拷贝构造函数,编译器会为子类提供缺省的拷贝构造函数,该函数会自动调用基类的拷贝构造函数,完成基类子对象的拷贝。
–》如果子类缺省的拷贝构造无法满足当前需求(比如有浅拷贝的问题),那么在自定义拷贝构造函数时,需要使用初始化列表显式指明基类子对象也要以拷贝方式进行初始化。
class 基类{};
class 子类:public 基类{
//基类(that):指明基类子对象以拷贝方式初始化
子类(const 子类& that):基类(that),…{}
};

2)子类的拷贝赋值
–》如果子类没有定义拷贝赋值函数,编译器会为子类提供缺省的拷贝赋值函数,该函数会自动调用基类的拷贝赋值函数,完成基类子对象的复制
–》如果子类缺省的拷贝赋值函数无法满足当前需求(比如有浅拷贝的问题),那么在自定义拷贝赋值函数时,需要显式调用基类的拷贝赋值函数,完成基类子对象的复制。
class 基类{};
class 子类:public 基类{
子类& operator=(const 子类& that){

//显式调用基类的拷贝赋值函数
基类::operator=(that);
}
};

猜你喜欢

转载自blog.csdn.net/weixin_43789711/article/details/90113535
今日推荐