c++的学习之路:20、继承(1)

摘要

本章主要是讲以一下继承的一些概念以及使用方法等等。

目录

摘要

一、继承的概念及定义

1、继承的概念

2、继承定义

1.2.1、定义格式

1.2.2、继承关系和访问限定符

1.2.3、继承基类成员访问方式的变化

3、总结

二、基类和派生类对象赋值转换

三、继承中的作用域

四、派生类的默认成员函数

五、继承与友元

六、继承与静态成员

七、代码

 八、思维导图


一、继承的概念及定义

1、继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保
持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继
承是类设计层次的复用。

这个继承形容的十分形象,就像子承父业继承家产,哈哈这个就是继承,但是呢在之前说类的时候就说了有三个类型,public、private、protected这三个类型,但是protected我却没咋用过,这里就使用,下面将会一一介绍。

2、继承定义

1.2.1、定义格式

如下方代码就是一个继承的定义,如图二就是测试结果,图一就是继承定义格式,这里public也可以不写,但是默认是私密形式,但是不也可以用struct就是共有的这里和类的定义有点相像。

class Person
{
protected:
    string _name = "张三";
    int _age = 18; 
};

class Student :public Person
{
protected:
    int _stuid;
};

1.2.2、继承关系和访问限定符

如下方两个导图就是所有的继承关系和访问限定符

1.2.3、继承基类成员访问方式的变化

下面表就是继承的基类成员访问方式的变化,就是根据这个表去写的权限。

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected
成员
派生类的private
成员
基类的protected
成员
派生类的protected
成员
派生类的protected
成员
派生类的private
成员
基类的private成
在派生类中不可见在派生类中不可见在派生类中不可

3、总结

1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public  > protected> private。

4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

二、基类和派生类对象赋值转换

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象。

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。

如下图所示这种切割方式,就是上述所说的切片。

三、继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

4. 注意在实际中在继承体系里面最好不要定义同名的成员

如下方代码想要访问B里面的fun就会访问不到,所以这里就可以看到下方代码中main的方式就可以访问了,结果如图。

class A
{
public:
    void fun()
    {
        cout << "func()" << endl;
    }
};
class B : public A
{
public:
    void fun(int i)
    {
        A::fun();
        cout << "func(int i)->" << i << endl;
    }
};

int main()
{
    /*Student s;*/
    B b;
    b.fun(10);
}

四、派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?

1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

5. 派生类对象初始化先调用基类构造再调派生类构造。

6. 派生类对象析构清理先调用派生类析构再调基类的析构。

7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

下面的导图就是六个默认成员函数。

五、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员,这中的解决方式也简单就是在子类在进行声明一下是友元就可以了,如下方代码所示,就是接着声明,要不就会使用不了。

class Person
{
public:
    friend void test(const Person& p, const Student& s);
protected:
    string _name; // 姓名
};
class Student : public Person
{
public:
    friend void test(const Person& p, const Student& s);
protected:
    int _stuNum; // 学号
};

六、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 ,这样就可以计算使用了多少次这个函数,可以用来测试创建了多少人,如下方代码和图片所示

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; 
};

int main()
{
    /*Student s;*/
    /*B b;
    b.fun(10);*/
    Student s1;
    Student s2;
    Student s3;
    Graduate s4;
    cout << " 人数 :" << Person::_count << endl;
    
}

七、代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//class Person
//{
//protected:
//	string _name = "张三";
//	int _age = 18; 
//};
//
//class Student :public Person
//{
//protected:
//	int _stuid;
//};
//
//class A
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//};
//class B : public A
//{
//public:
//	void fun(int i)
//	{
//		A::fun();
//		cout << "func(int i)->" << i << endl;
//	}
//};

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; 
};


//class Person
//{
//public:
//	friend void test(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名
//};
//class Student : public Person
//{
//public:
//	friend void test(const Person& p, const Student& s);
//protected:
//	int _stuNum; // 学号
//};


int main()
{
	/*Student s;*/
	/*B b;
	b.fun(10);*/
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	
}

 八、思维导图

  • 34
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值