继承的总结

基础知识:

|定义:在已有类的基础上创建新类的过程;

B类继承A类内容,称A类为基类(父类),B类为派生类(子类);

称类A派生类B;

 

|语法形式:

class 派生类(子类):基类名表

{

数据成员和成员函数声明;

 

};

|基类名表 构成

访问控制 基类名1,访问控制 基类名2,访问控制 基类名n;

 

|访问控制 表示派生类对基类的继承方式,使用的关键字:

public 公有继承

private 私有继承

protected 保护继承

 

无论何种继承方式,派生类都不能直接使用基类的私有成员;

 

|派生类的生成过程:

1、吸收基类成员(除构造、析构外,全部吸收,不一定可见);

2、改造基类成员(通过在派生类中定义同名成员函数和数据成员,可屏蔽(隐藏)在派生类中不起作用的部分基类成员);

3、添加派生类的新成员(需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现);

 

|基础样例:

 

#include <iostream>

using namespace std;
class A
{
public:
int a;
int b;
private:
int c;
protected:
int d;
};
class B: protected A
{  
    int c;      };
    int
main( )
    {
    
     cout << "size of A is"<< sizeof(A);
     cout << "size of B is" << sizeof(B);

 }

 

公有继承:

基类

派生类

私有成员

 

公有成员

公有成员

保护成员

保护成员

 

私有成员

 

保护成员

 

公有成员

 

 

样例:

定义基类person(无构造函数)

数据成员:姓名、性别、年龄(私有成员)

定义公有成员函数set()/dispaly()

 

再定义学生类为派生类(采用公有继承)

增加学号、班级、专业、入学成绩

定义公有成员函数set()/display()

代码样例:

#include<iostream>
#include <string>
using namespace std;
class Person
{

string name;
int age;
string sex;
public:
void set_p() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
void show_p() {
  cout<<name<<"  "<<age<<"  "<<sex<<endl;
}
};


class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set_t(){
       set_p(); //调用继承于基类的成员函数访问继承于基类的私有数据成员,当基类和派生类函数名相同时可用Person::基类函数名,调用基类函数
    cout<<"zhuanye\tt_class\tscore"<<endl;
    cin>>zhuanye>>t_class>>score;
}
void show_t() {
show_p();//当基类和派生类函数名相同时可用Person::基类函数名,调用基类函数
cout<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;
}
};

|重名函数:

当在派生类中定义了与基类同名的成员,在派生类中访问时,派生类中的函数起作用;

可用基类名::成员的形式调用被屏蔽的同名函数;

例:

class  base
  { public :
           int  a ,  b ;  
  } ;
class  derived : public  base
  { public :  
         int  b ,  c ; 
  } ;
void  f ()
{ derived  d ;
   d . a = 1 ;
   d . base :: b = 2 ;
   d . b = 3 ;
   d . c = 4 ;
};

base

a

b

 

 

derived

a

b

b

c

 

 

|派生类中访问静态成员:

基类中若定义静态成员,将被所有派生类共享(包括基类);

派生类中访问静态成员的形式:

类名::成员;

对象名.成员;

 

样例:

#include<iostream>

using namespace std ;

class B

{ public:

    static void Add() { i++ ; }

    static int i;

    void out() { cout<<"static i="<<i<<endl; }

};

int B::i=0;

class D : private B

{ public:    

      void f()

       { i=5;

         Add();

         B::i++;

         B::Add();

       }

};

int main()

{ B x;  D y;

  x.Add();

  x.out();

  y.f();

  cout<<"static i="<<B::i<<endl;

  cout<<"static i="<<x.i<<endl;

  //cout<<"static i="<<y.i<<endl;

}

运行结果:

static i=1

static i=8

static i=8

 

|基类的初始化:

在构建派生类对象时,用指定参数调用基类的构造函数来初始化派生类继承基类的数据;

派生类构造函数的声明形式为:

派生类构造函数(变元表):基类(变元表),对象成员1(变元表)...

构造函数的执行顺序为:基类、对象成员、派生类;

样例:

#include<iostream>
using namespace std ;
class  parent_class
{     int  data1 , data2 ;
   public :
       parent_class ( int  p1 , int  p2 ) { data1 = p1; data2 = p2; }
       int  inc1 () { return  ++ data1; }
       int  inc2 () { return  ++ data2 ; }
       void  display  ()  {cout << "data1=" << data1 << " , data2=" << data2 << endl ; }
};
class  derived_class : private  parent_class
{     int data3 ;
       parent_class  data4 ;
   public:
       derived_class ( int  p1 , int  p2 , int  p3 , int  p4 , int  p5 ): parent_class ( p1 , p2 ) , data4 ( p3 , p4 )
           { data3 = p5 ; }
       int  inc1 ( ) { return  parent_class :: inc1 ( ) ; }
       int  inc3 ( ) { return  ++ data3 ; }
       void  display ( )
          { parent_class :: display ( ) ;   data4.display ( ) ;
             cout << "data3=" << data3 << endl ;
          }
} ;
int main ( )
{ derived_class  d1 ( 17 , 18 , 1 , 2 , -5 ) ;  

 d1 . inc1 ( ) ;   

  d1 . display ( ) ;  }

派生类构造函数析构函数的使用原则:

1、基类的构造函数和析构函数不能够被继承;

2、若基类中无构造函数或有无参函数,派生类中也不可以定义构造函数;

3、如果基类没有无参函数的构造函数,派生类必须定义构造函数;

4、若派生类的基类也是派生类,则每个派生类只负责直接相关的基类的构造;

5、派生类是否定义析构函数与所属的基类无关;

 

|派生类的数据成员既包括基类的数据成员,也包括派生类新增成员;

 

如何在派生类中对基类成员进行初始化?

可调用基类构造函数对基类成员进行初始化;

 

派生类构造函数的一般形式:

  派生类::派生类名(参数总表):基类名(参数表)
     {
             派生类新增成员的初始化语句;
     }

1、当派生类无对象成员时:

创建派生类对象时,构造函数的执行顺序为:基类的构造函数、派生类的构造函数;

撤销派生类对象时,执行顺序:派生类的析构函数、基类的析构函数;

2、当派生类中含有对象成员时:

创建派生类对象时,执行顺序:基类的构造函数、对象成员的构造函数、派生类的构造函数;

撤销派生类对象时,执行顺序:派生类的构造函数、对象成员的构造函数、基类的构造函数;

样例:

#include<iostream.h>
class base
{
public:
base(){cout<<"constructing base class"<<endl;}
~base(){cout<<"destructing base class"<<endl; }
};
class subs:public base
{
public:
subs(){cout<<"constructing sub class"<<endl;}
~subs(){cout<<"destructing sub class"<<endl;}
};
void main()
{
subs s;
} 
运行结果:

constructing base class
constructing sub class
destructing sub class
destructing base class

|多继承:

定义:一个类由多个直接基类的继承关系称为多继承;

多继承声明形式:

class  派生类名 : 访问控制  基类名1 ,  访问控制  基类名2 ,  … , 访问控制  基类名n
    {
         数据成员和成员函数声明
    };

 

|多继承派生类的构造和访问:

多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员。
执行顺序与单继承构造函数情况类似;

多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序;
一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性;

如果不同的基类有同名成员,派生类对象访问时应该加以识别;

多继承的构造函数: 

派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
    {
           // 派生类新增成员的初始化语句
    } 

多继承方式下构造函数的执行顺序:
先执行所有基类的构造函数
再执行对象成员的构造函数
最后执行派生类的构造函数
       

处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序,与派生类构造函数中所定义的成员初始化列表顺序没有关系,
内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致。

 

析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同;
功能是在派生类中对新增的有关成员进行必要的清理工作;
析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理;

|赋值兼容规则:

指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代;

所指的替代包括以下情况:

 派生类的对象可以赋给基类对象;
 派生类的对象可以初始化基类的引用;
 派生类的对象的地址可以赋给基类类型的指针;

|赋值兼容的可行性:

1、通过公有继承:

派生类得到了除了构造、析构函数以外的所有成员;
且这些成员的访问控制属性也和基类完全相同;
这样,它便具备了基类的所有功能;

2、利用赋值兼容的规则:

派生类的对象可以赋给基类对象(强制类型转换);(Base b;Derived d;b=d;)
派生类的对象可以初始化基类的引用(Derived d;Base &br=d;)
派生类的对象的地址可以赋给基类类型的指针(Derived d;Base*bptr=&d;)

可以把指向派生类对象的指针赋值给基类对象的指针;(Derived *dptr,obj;dptr=&obj;Base *bptr=dptr;)

|在替代后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员;

 

二、心得体会:

继承的应用,可以让我们更好的理顺我们的逻辑思路,减少冗余的代码,简化代码的篇幅;

继承中,基类与派生类的关系如同父子一般,顾名思义,即为儿子从父亲那里继承其内容,后有扩充自己的内容;

继承并不是单纯的一对一继承,可以一对多,也可以多对一的多继承,一对多即为想使俩个类所拥有的内容有相同的部分,可继承同一个类,多对一则为一个类继承包含多个类的内容;

要想应用好继承,首先要缕清自己的思路,想好其中的逻辑关系,而且要课下多实践,才能真正的发现盲区,以及可以熟练知识;

### 类的基本概念 类是面向对象编程中的核心概念,它是一种用户自定义的数据类型,用于封装数据和操作数据的方法。类定义了对象的属性和行为,对象是类的实例。例如: ```csharp public class Person { public string Name; public int Age; public void SayHello() { Console.WriteLine($"大家好,我是{Name},我{Age}岁了。"); } } ``` ### 类的继承 继承是面向对象编程中的重要概念,允许一个类继承另一个类的属性和行为。被继承的类称为基类或父类,继承这个类的类称为派生类或子类。派生类可以继承基类的成员,并可以添加新的数据成员和函数成员,或者修改已继承的成员[^1]。 ```csharp public class Person { public string Name; private int Age; public void SayHello() { Console.WriteLine($"大家好,我是{Name},我是人类"); } } public class Student : Person { public string School; public void Study() { Console.WriteLine($"{Name}正在{School}学习。"); } } class Program { static void Main(string[] args) { Student stu = new Student(); stu.Name = "李明"; stu.School = "XX中学"; stu.SayHello(); // 输出:大家好,我是李明,我是人类 stu.Study(); // 输出:李明正在XX中学学习。 } } ``` ### 继承的特点 - **访问权限**:子类继承父类的属性、方法等,但子类没有继承父类的私有(private)字段。 - **密封类**:为避免继承的滥用导致类的层次结构庞大、关系混乱,C# 提供了密封类,只需在父类前加上 `sealed` 修饰符,该类将不能被继承。密封方法也是在方法前加上 `sealed` 修饰符[^2]。 ### 抽象类和接口相关继承 - **接口继承接口**:接口可以继承接口,这种继承关系被称为接口继承。通过接口继承,一个接口可以继承另一个或多个接口的成员,包括方法、属性、事件和索引器,使接口之间的关系更灵活,提高代码的可重用性。 - **抽象类实现接口**:抽象类可以实现接口,通过实现接口,抽象类可以定义和实现接口中的成员,且必须提供接口中定义的所有成员的具体实现,使抽象类具备接口所定义的行为,并可在派生类中重写这些行为。 - **抽象类继承实体类**:抽象类可以继承实体类,但该抽象类必须有明确的构造函数。实体类是指具体实现了所有成员的类,抽象类是一种不能被实例化的类,只能被继承。通过继承实体类,抽象类可以继承实体类中的成员,并可在派生类中添加新的成员或者重写基类中的成员[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值