细数继承与派生(纯干货推荐)1.0

Posted on 2021-03-25 17:56  sesen  阅读(225)  评论(0编辑  收藏  举报

目录

    • 一、类与对象的基础
      • 1、简单概念
      • 2、类的类型
        • ①、公有
        • ②、私有
        • ③、保护
      • 4、扩展:(类和结构体的区别)
    • 二、使用类和对象
      • 方法一:
      • 方法二:
      • 方法三:
    • 三、继承与派生
      • 1、名称解释
      • 2、单一继承
        • ①、公有继承
        • ②、私有继承
        • ③、保护继承
        • ④、总结
      • 3、多重继承
      • 4、注意事项
        • ①、相互赋值
        • ②、避免二义性

 

最近在学习C++面向对象设计,看似很基础的内容实际上有多知识点值得推敲。

学完类与对象,就很想写点东西来输出我的思考和学习的想法,也希望各位朋友一起多多交流,如果文章里有错误的地方请多多指正。

一、类与对象的基础

前言:

学习C,要有面向过程的设计思想。

学习C++,要有面向对象的设计思想。

1、简单概念

对象: 任何一个事物都可以看成一个对象,比如一个班级,一个学校,一篇文章等等。

对象包含两个要素:属性 和 行为。

举个栗子:我们把一个班级看做一个对象,那么班级里的人数,专业,平均成绩叫做属性(静态的)。班级上课,参加运动会等叫做行为(动态的)。一个对象一般是由一组属性和一组行为构成。

封装 :一般对象里的内容会涉及到秘密,在面对外人时。我们可以把对象里的部分内容盖起来不让外人看到。

举个栗子:班级期末考试,家长想知道班级里所有人的成绩(家长对班级这个对象进行访问/调用),但我们只告诉家长班级平均成绩,所有的同学的成绩都不对外公布(个人成绩进行封装)。相反,平均分就是属于大家都能看到/接触/调用的内容。

2、类的类型

①、公有

听名字就知道,这是个大家都能看得到或者接触得到的内容。

比如上个栗子中的班级平均分就属于公有部分,班级中的每位家长都可以看到。

class Student
{
	public:
	```
	```
}


②、私有

听名字就知道属于定义对象所拥有的部分,不能让外界看到或者解除(当然也会有例外,这里挖个坑)。把部分成员进行封装,防止外人看到。

上个栗子中对应的是班级里每位同学的成绩。

class Student
{
	private:
	```
	```
}

③、保护

首先一个问题,保护类型和私有类型有什么区别? 这里我们先做个简单的介绍:

私有和保护的成员函数和数据只能被本类中的其他成员函数所调用,而不能被类外调用。 而保护的功能在继承与派生中更能体现出来, 当然也有例外的情况发生。同私有类型一样,挖个坑。 写完例外情况(友元),我会在此处做个文章链接。

class Student
{
	protected:
	```
	```
}

4、扩展:(类和结构体的区别)

先上两种定义:(Student为例)

///class
class Student
{
	private:
		int num;
		char sex:
		string name;
	public:
		void display();
};

///struct(C)
struct Student
{
		int num;
		char sex:
		string name;	
}stu;


void display(int num,char sex,string name);


补充一个小知识:C++在设计的时候就以兼容C,但又不是完全简单的继承C,同时还要有自己的特点为原则进行开发。

最明显的就是struct中没有private和public,想给student添加什么属性直接写进去就完事了。

class不同,除了想把属性加进去外,还要考虑这个属性要不要让别人看到。想让别人能看到,加入public中。不想让别人看到就加入private中。

举个栗子: 如果把struct比作一家新百伦鞋店,那么这家店就属于通透型的,穿鞋换鞋坐在沙发板凳上就可以完成,无需试衣间。所有的地方一览无余。 class更像是含有试衣间的优衣库。挑选衣服的时候大家都能看到,但是试衣服的时候就必须要去试衣间里。我想没有多少人在换衣服的时候被别人看到吧?

以上内容引用于比较C中的struct和C++中的class。

而在C++中对于struct的用法进行了升级:

///struct(C++)
struct Student
{
	private:
		int num;
		char sex:
		string name;
	public:
		void display();
};

可见C++中使用struct也可以像class一样对成员进行public或private分类。

此时他们的区别就是对于未声明的成员进行默认分类。

///class 
class Student
{
		int num;
		char sex:
		string name;
		///三种成员被默认为private类型
		///进行类内访问,类外不能访问
};

///struct(C++)
struct Student
{
		int num;
		char sex:
		string name;
		///三种成员默认为public类型
		///类内类外均可以访问
};		

二、使用类和对象

设计好了我们所需要的类,接下来就是定义对象。

方法一:

class Student stu1,stu2;///class Student合起来作为一个整体类名,来定义对象

Student stu1,stu2;///或者直接使用Student定义对象,与结构体定义对象相同

方法二:

class Student
{
	private:
		int num;
		char sex:
		string name;
	public:
		void display();
}stu1,stu2;					///与结构体定义相同

方法三:

class 
{
	private:
		int num;
		char sex:
		string name;
	public:
		void display();		///这可以不通过类名直接定义对象,但是不推荐使用。结构体中也是这样
}

三、继承与派生

1、名称解释

构造函数

我们通过构造函数来使对象的属性进行初始化。如果想添加初始值,可以手动设置。反之可以交给编译器自动指定默认值。 建立好类之后,一般我们需要对其进行初始化。这是为什么呢?就像我们注册某个网站时,填写好注册名称,密码进行默认选择一样。在登录之后会要求进行修改密码以保证登录的私密性。

析构函数

如果说构造函数是为了方便用户对类进行初始化。那么析构函数就是在你完成所有程序功能,帮你进行收尾后勤工作。此时你不用管设置了多少个属性,开辟了多少内存空间。只要析构函数执行,就能帮你完成一切。我更愿意称之为妈妈式的程序。

继承

聊下继承(inheritance),这个单词也有遗传的意思。那么就看出来继承或遗传的东西有前辈的内容,也会有自己独有/新添加的内容。

派生

在原来已经存在的类的基础上建立一个新的类。这个过程叫做父类派生出子类。 子类继承了父类的成员内容,也会存在着属于自己新添加的成员内容。

基类

又名:父类。 已经存在设计好的类型。(与下面的派生类做对应记忆)

派生类

又名:子类。 通过继承之前设计好的类,得到新建立的类。

2、单一继承

派生类中的成员包括了从基类继承过来的成员和自己新增加的成员两部分。

①、公有继承

对基类成员内容通过公有方式继承得到的派生类。

///基类
class Student
{   int num;						//默认为private类型
    char name[30];
    char sex;
 public:                             
   void display( )                    //对成员函数display的定义
    {cout<<"num: "<<num<<endl;
     cout<<"name: "<<name<<endl;
     cout<<"sex: "<<sex<<endl; } 
};

///派生类
class Student1: public Student//声明基类是Student
{
private:
    int age;    //新增加的数据成员
    string addr;  //新增加的数据成员
public:
   void display_1( )  //新增加的成员函数
   {  
   	cout<<"age: "<<age<<endl; 
   	cout<<"address: "<<addr<<endl;
	}   
};

补充: 在建立派生类的过程中,基类不会做任何改变。派生类除了从基类中得到成员内容,也可以重新定义属于自己的变量与函数。

例如:父类对象为“马”,含有的成员变量有马的性别,年龄等。我们可以对马进行更加细致的划分,建立派生类汗血宝马(添加新的成员变量:跑步里程),蒙古马(添加新的成员变量:毛发长度),大宛马(添加新的成员变量:一顿能吃多少个馒头)等等。这样就就与派生类就与基类有了分别。

那么通过公有继承的方式,可以从派生类中访问基类的内容吗?当你脑海中蹦出这个问题时就说明已经学会举一反三了。

公有派生时,基类中的所有成员在派生类中的保持各个成员的访问权下,如下图在这里插入图片描述另外上个图能让大家更好理解在这里插入图片描述

上述图片中(a)为基类(父类),(b)为派生类(子类),(c)是整理过后的派生类。可以看出通过公有继承图(c)中已经没有了类A的private变量k。这是因为通过公有继承,对父类的私有变量已经进行了封装,无法在派生类中访问(不可见)。但是可以通过类A中的成员函数引用到private中的k。

通过公有继承中,派生类想对基类中的private成员不可以引用,那我们就是想引用怎么办?

这里就是前面说的特例——友元。 挖个坑!!!

下面是个公有继承的小实例,有时间的读者可以认真看一下,代码很简单。

#include <bits/stdc++.h>

using namespace std;

class Student
{   int num = 12138;						//默认为private类型
    char name[30] = "ZhangSan" ;
    char sex = 'M';
 public:
   void display( )                    //对成员函数display的定义
    {cout<<"num: "<<num<<endl;
     cout<<"name: "<<name<<endl;
     cout<<"sex: "<<sex<<endl; }
};

class Student1: public Student//声明基类是Student
{private:
    int age = 18;    //新增加的数据成员
    string addr = "China";  //新增加的数据成员
public:
   void display_1( )  //新增加的成员函数
   {  	
   		cout<<"age: "<<age<<endl;
        cout<<"address: "<<addr<<endl;
    }
};
///ac不知深
int main()
{
    Student stu1;
    Student1 stu2;
    cout << "stu1直接输出" << endl;
    stu1.display();
    cout << "stu2直接输出" << endl;
    stu2.display_1();
    cout << "stu1通过访问stu1输出" << endl;
    stu2.display();
    return 0;
}

在这里插入图片描述

②、私有继承

对基类成员内容通过私有方式继承得到的派生类。基类中public成员和protected成员会在派生类中都变成私有的。但是在派生类中仍然可以直接使用这些成员。基类中的private成员在派生类中依然不能直接使用。

///基类
class Student
{   int num;						//默认为private类型
    char name[30];
    char sex;
 public:                             
   void display( )                    //对成员函数display的定义
    {
    	cout<<"num: "<<num<<endl;
     	cout<<"name: "<<name<<endl;
     	cout<<"sex: "<<sex<<endl; 
     } 
};

///派生类
class Student1: private Student//声明基类是Student
{
private:
    int age;    //新增加的数据成员
    string addr;  //新增加的数据成员
public:
   void display_1( )  //新增加的成员函数
   {  
   		cout<<"age: "<<age<<endl; //引用派生类的私有成员
   		cout<<"address: "<<addr<<endl;	//引用派生类的私有成员
	}   
	
	void get_value_1()
	{
		cin >> num >> name >> sex ;		//输入派生类数据
	}
};

和公有继承是的原理都是相似的。

私有派生时,基类中的所有成员在派生类中的保持各个成员的访问权下,如下图在这里插入图片描述同样上个图让大家印象更深刻上述图片中(a)为基类(父类),(b)为派生类(子类),(c)是整理过后的派生类。可以看出通过私有继承图(c)中已经没有了类A的private变量k。这是因为通过私有继承,对父类的私有变量已经进行了封装,无法在派生类中访问(不可见)。同时,类A中的public与protected类型均转变为类B的private类型。

由此我们可以看到,私有继承似乎更霸道些。对于基类继承来的成员,派生类外是都不可以访问的。这种继承方式更倾向于保护隐私内容,不想让其他程序员知道我们自己定义的内容。

下面这个实战小例子,通过私有继承,可以很好与公有继承实例做个比较——无法通过类外访问私有继承的成员函数。

#include <bits/stdc++.h>

using namespace std;

class Student
{   int num = 12138;						//默认为private类型
    char name[30] = "ZhangSan" ;
    char sex = 'M';
 public:
    void display( )                    //对成员函数display的定义
    {cout<<"num: "<<num<<endl;
     cout<<"name: "<<name<<endl;
     cout<<"sex: "<<sex<<endl; }
};

class Student1: private Student//声明基类是Student
{private:
    int age = 18;    //新增加的数据成员
    string addr = "China";  //新增加的数据成员
public:
   void display_1( )  //新增加的成员函数
   {    cout<<"age: "<<age<<endl;
        cout<<"address: "<<addr<<endl;
    }
};
///ac不知深
int main()
{
    Student stu1;
    Student1 stu2;
    cout << "stu1直接输出" << endl;
    stu1.display();     ///可以通过stu1直接访问基类
    cout << "stu2直接输出" << endl;
    stu2.display_1();   ///可以通过stu2直接访问派生类
    ///cout << "stu1通过访问stu1输出" << endl;
    ///stu2.display();     //本语句错误,无法通过stu2访问基类函数
    return 0;
}


在这里插入图片描述

③、保护继承

保护成员和私有成员在基类外都是不能被访问的,可以认为保护成员对类来说是私有的。但是保护成员可以被派生类的成员函数引用。这是两者比较明显的区别。在这里插入图片描述大家看到这个图一定觉得很迷惑,这个图明明和私有继承是一样的啊?

没错,但是protected 成员是一种具有血缘关系内外有别的成员。它对派生类的对象而言,是公开成员,可以访问,对血缘外部而言,与私有成员一样被隐蔽。但事无绝对,这里再一次挖个坑——友元。

在举个栗子,private更像是你们家的存折户口本房产证等,一般都由父母(父类可以访问)放在比较隐蔽的地方。即使是作为孩子的你(派生类)也不知道(无法访问),更不用说你的邻居朋友(类外无法访问)了。protected更像是你父母抽屉里的指甲剪,父母(父类可以访问)知道在哪,你也知道在哪(派生类可以访问)。但是朋友邻居(类外无法访问)就不知道在哪里。

还是迷迷糊糊的朋友直接看下面一张图!!! 下面一张图给你解释清楚!!!在这里插入图片描述类A分别通过private和protected继承得到B和C,再同时public继承得到B1和C1。 这个时候私有继承和公有继承的区别就显现出来了:

B1无法访问A中的public与protected成员;

C1可以访问A中的public与protected成员

接下来简单做个总结比较:

保护继承私有继承
public: (变为保护)在派生类中使用,类外不可使用 public: (变为私有)在派生类中使用,类外不可使用
protected: (变为私有)在派生类中使用,类外不可使用 protected: (变为私有)在派生类中使用,类外不可使用
private: 不能在派生类中和类外使用 private: 不能在派生类中和类外使用

来个实战小例子

#include <bits/stdc++.h>

using namespace std;

class Student
{
protected:
    int num;
    char name[30];
    char sex;
 public:
                            ///基类无公用成员
};

class Student1: protected Student//声明基类是Student
{private:
    int age;    //新增加的数据成员
    string addr;  //新增加的数据成员
public:
    void get_value1()
    {
        cin >> num >> name >> sex;      //输入保护基类数据成员
        cin >> age >> addr;             //输入派生类数据成员
    }

    void display_1( )  //新增加的成员函数
    {
        cout<<"num: "<<num<<endl;       //引用基类的保护成员
     	cout<<"name: "<<name<<endl;     //引用基类的保护成员
     	cout<<"sex: "<<sex<<endl;       //引用基类的保护成员
        cout<<"age: "<<age<<endl;       //引用派生类的私有成员
        cout<<"address: "<<addr<<endl;  //引用派生类的私有成员
    }
};
///ac不知深
int main()
{
    Student1 stu1;      //stu1是派生类Student1类的对象
    stu1.get_value1();  //get_value1是派生类中的公有成员函数,输入数据
    stu1.display_1();   //display1是派生类中的公有成员函数,输出数据
    return 0;
}

在这里插入图片描述

④、总结

最后我们做一个基类成员在派生类中的访问属性比较

在基类的访问属性继承方式在派生类中的访问属性
private(私有) public(公有) 不可访问
private(私有) private(私有) 不可访问
private(私有) protected(保护) 不可访问
public(公有) public(公有) public(公有)
public(公有) private(私有) private(私有)
public(公有) protected(保护) protected(保护)
protected(保护) public(公有) protected(保护)
protected(保护) private(私有) private(私有)
protected(保护) protected(保护) protected(保护)

派生类中的访问属性有四种:不可访问,public,private,protected。

①不可访问,派生类内类外都不能访问;

②public,派生类内类外都可以访问;

③private,派生类内可以访问,派生类外不能访问;

④protected,派生类内可以访问,派生类外不能访问,其新派生出的下一代的派生类可以访问。

3、多重继承

前面讨论的是单一继承。实际上,常常有一个派生类有两个或多个基类,派生类从两个或多个基类中继承所需的属性。比如,你的白天在公司扮演者普普通通的码农,晚上扮演着媳妇的好老公。你的身份就可能需要继承多个基类才能更好更完善的刻画出你的形象。代码更好的向前一步发展,我们需要多重继承。

这里我觉得没有太多难点,只要单一继承弄明白了多重继承分分钟就ok!

class  D: public  A, protected B, private C 
{    ....//派生类中新增加成员
};

这里就直接给大家一个简单的例子进行学习

class  A
{
public:	
	int x;
	void Show(){cout <<"x="<<x<<'\n';}
};

class B
{
public:	
	int y;
	void Show(){cout <<"y="<<y<<'\n';}
};

class C:public A,public B
{
	public:	
		int y; //类B和类C均有y的成员
};

void  main(void)
{  	
	C c1;	           
	c1.x=100;
    c1.y=200;	//给派生类中的y赋值
    c1.B::y=300;	//给基类B中的y赋值
    c1.A::Show();		
    c1.B::Show();	//用作用域运算符限定调用的函数
    cout <<"y="<<c1.y<<'\n';	//输出派生类中的y值
    cout <<"y="<<c1.B::y<<'\n';	//输出基类B中的y值
}

4、注意事项

①、相互赋值

这里其实真正的叫法是基类与派生类的转换。 这个篇幅太长(主要是我懒),我们放在下篇文章继续更新!

②、避免二义性

多种继承能够更好地处理比较复杂的问题。比如一个在职的研究生老师张三,张三属于教师类,属于研究生类,也属于一个人类(废话hhhh),我们做个简单的定义。

class Teacher       //教师    
{
public:
    void display();
protected:
    string name;    //姓名
    int age;        //年龄
    string title;   //职务
    
};


class Student
{
public:
    void display1();
protected:
    string name;    //年龄
    char sex;       //性别
    float score;    //分数
};


class Graduate:public Teacher,public Student
{
public:
    void show();
private:
    float wage;     //补贴
};
///ac不知深

这里我们看到在类Teacher和类Student中都有姓名成员,如果只是单纯的继承会导致Graduate中存在两个name函数,初始化赋值时一定会报错。具体怎么解决这个问题,麻烦各位读者点赞和关注,我会尽快更新这部分的知识并在下面放上链接!

如果有帮助,可以一键三联呀!!!

本文转自:SDK社区(sdk.cn)是一个中立的社区,这里有多样的前端知识,有丰富的api,有爱学习的人工智能开发者,有风趣幽默的开发者带你学python,还有未来火热的鸿蒙,当各种元素组合在一起,让我们一起脑洞大开共同打造专业、好玩、有价值的开发者社区,帮助开发者实现自我价值!

Copyright © 2024 sesen
Powered by .NET 9.0 on Kubernetes