C++继承

一.基本概念:
1.一个类可以继承另一个类,继承的类称为派生类(子类)、被继承的类称之为基类(父类)。
2.子类继承了父类所有成员,除了构造函数和赋值运算符重载。
3.子类继承父类后,子类的成员分为两部分(1)继承之父类的部分base part(2)子类自己扩展的部分 appendent part
4.子类继承父类后,子类依然不能访问父类的私有成员,子类只能通过继承之父类的公有成员来访问父类的私有成员。
5.子类可以实现与父类成员函数原型相同的函数称为重写(覆盖)overwrite。
6.子类调子类的,父类不能调子类的,子类可以调用被覆盖的父类版本的函数,使用子类对象名.父类名::函数名,比如:s.farther::print();
7.子类通过父类的私有成员的两种方式:
(1)通过父类的公有成员函数
(2)将私有成员改成保护成员protected
二.继承的使用
1.继承使用的两种情况:
(1)类之间有自然的继承关系,一个类是另一个类的特例,比如:一个学生是一个人。
(2)实现代码复用,一个类需要使用一个类的成员时。
2.继承的好处:
(1)代码复用
(2)使代码修改更容易
3.两个类之间的交互关系
(1)组合类 一个类中有另一个类的对象 has-a
(2)继承类 一个类是另一个类的特例 is-a
三.c++的三种继承
(1)公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
(2)保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
(3)私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
**总结:**private能够对外部和子类保密,即除了成员所在的类本身可以访问之外,别的都不能直接访问。protected能够对外部保密,但允许子类直接访问这些成员。

class Base    //父类
  {
    private: 
         int _priB;
     protected:
         int _proB;
     public:
        int _pubB;
  } ;
 class Derived: public Base    //子类,继承自base,继承类型为公有继承
 {
    private:
       int _d_pri;
    protected:
       int _d_pro;
    public:
        void funct()
     {
         int d;
         d=_priB;       //error:基类中私有成员在派生类中是不可见的
         d=_proB;       //ok: 基类的保护成员在派生类中为保护成员
         d=_pubB;       //ok: 基类的公共成员在派生类中为公共成员
     }
       int _d_pub;
 } ;
总结:(1). public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用, 因为每个子类对象也都是一个父类对象。
 class C :private Base    //基类Base的派生类C(私有继承)
 {
 public:
     void funct()
     {
         int c;
         c=_priB;      //error:基类中私有成员在派生类中是不可见的
         c=_proB;      //ok:基类的保护成员在派生类中为私有成员
         c=_pubB;      //ok:基类的公共成员在派生类中为私有成员
     }
 };
 class E :protected Base   //基类Base的派生类E(保护继承)
 {
 public:
     void funct()
     {
         int e ;
         e=_priB;    //error:基类中私有成员在派生类中是不可见的
         e=_proB;    //ok:基类的保护成员在派生类中为保护成员
         e=_pubB;    //ok:基类的公共成员在派生类中为保护成员
     }
 };
总结:
(2). 基类的private成员 在派生类中是不能被访问的, 如果基类成员 不想在类外直接被访问, 但需要 在派生类中能访问, 就定义为protected。 可以看出保护成员 限定符是因继承才出现的。
(3). protected/private继承是一个实现继承, 基类的部分成员 并非完全成为子类接口 的一部分, 是 has-a 的关系原则, 所以非特殊情况下不会使用这两种继承关系, 在绝大多数的场景下使用的 都是公有继承。 私有继承以为这is-implemented-in-terms-of(是根据……实现的) 。 通常比组合(composition) 更低级, 但当一个派生类需要访问 基类保护成员 或需要重定义基类的虚函数时它就是合理的。
 int main()
 {
     int a; 
     D d;
     a=D._priB;     //error:公有继承基类中私有成员在派生类中是不可见的,对对象不可见     
     a=D._proB;           //error:公有继承基类的保护成员在派生类中为保护成员,对对象不可见
     a=D._pubB;     //ok:公有继承基类的公共成员在派生类中为公共成员,对对象可见
     C c;
     a=c._priB;    //error:私有继承基类中私有成员在派生类中是不可见的, 对对象不可见
     a=c._proB;    //error:私有继承基类的保护成员在派生类中为私有成员,对对象不可见
     a=c._pubB;    //error:私有继承基类的公共成员在派生类中为私有成员,对对象不可见
     E e;
     a=e._priB;    //error:保护继承基类中私有成员在派生类中是不可见的, 对对象不可见
     a=e._proB;    //error:保护继承基类的保护成员在派生类中为保护成员,对对象不可见   
     a=e._pubB;    //error:保护继承基类的公共成员在派生类中为保护成员,对对象不可见

     return 0;
 }
(4). 不管是哪种继承方式, 在派生类内部都可以访问基类的公有成员和保护成员 , 基类的私有成员存在但是在子类中不可见( 不能访问) 。
(5). 使用关键字class时默认的继承方式是private, 使用struct时默认的继承方式是public, 不过最好显式的写出继承方式。
(6). 在实际运用中一般使用都是public继承, 极少场景下才会使用protetced/private继承。

在struct继承中,如果没有显式给出继承类型,则默认的为public继承;在class继承中,如果没有显式给出继承类型,则默认的为private继承;
四.派生类的默认成员函数
1.构造与析构的调用关系
先看一段代码:

class Person
{
public:
	Person(const char* name = "hello")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	~Person() // ~destructor
	{
		cout << "~Person()" << endl;
	}
	
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
Student(const char* name = "helo")
		:_id(10)
		,Person(name)
	{
		cout << "Student(const char* name)" << endl;
	}
~Student()  // ~destructor  同名隐藏,编译器底层对析构函数的名字做了修改,为了使用多态
	{
		cout << "~Student()" << endl;
		//Person::~Person();
	}
private:
	int _id;
};
int main()
{
	Student s;
	return 0;
}

结果如下:
Person()
Student(const char* name)
~Student()
~Person()

分析:基类是派生类的一部分,创建派生类对象时必须调用派生类构造函数,而派生类构造函数必须使用基类的构造函数。程序首先创建基类对象,所以基类对象在程序进入派生类构造函数之前被创建。实际上C++使用成员初始化列表语法来完成这项工作,即Person()相当于在函数Student()的初始化列表中被使用,如果不调用基类构造函数,程序将使用默认的基类构造函数。在执行完Person()的函数体之后,继承的数据成员被初始化,执行Student()函数体初始化新增的数据成员。析构对象时,先调用派生类的析构函数,执行完函数体析构完新增部分之后,使用基类的析构函数析构继承自基类的部分。

总结:创建派生类对象时程序调用派生类构造函数,然后在初始化列表部分调用基类构造函数初始化继承的数据成员,而派生类构造函数主要初始化新增的数据成员。派生类总是调用一个基类构造函数。可以使用初始化列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数。派生类对象过期时,程序将先调用派生类析构函数,在函数体执行完之后调用基类析构函数。(可以看到,继承的数据成员生命周期长, 新增的数据成员生命周期短。)
2.拷贝构造函数
使用情况:
1.将新的对象初始化为一个同类对象
2.按值将对象传递给函数
3.函数按值返回对象
4.编译器生成临时对象
注意:如果基类中没有指针成员,可以使用浅拷贝,如果有指针成员,必须要进行显式定义拷贝构造函数,即进行深拷贝。不然会造成同一块内存空间被析构两次的问题。
3.赋值操作符
默认的赋值操作符用于处理同类对象之间的赋值,赋值不是初始化,如果语句创建新的对象,则使用初始化,如果语句修改已有对象的值,则为赋值。 赋值运算符是不能被继承的,原因很简单。派生类继承的方法的特征与基类完全相同,但赋值操作符的特征随类而异,因为它包含一个类型为其所属类的形参。
注意:赋值运算和拷贝构造是不同的,赋值是赋值给一个已有对象,拷贝构造是构造一个全新的对象
将派生类赋值给基类对象:
在这里插入图片描述
基类对象赋值给派生类对象:
在这里插入图片描述
总结:
是否可以将基类对象赋给派生类对象,答案是也许。如果派生类包含了转换构造函数,即对基类对象转换为派生类对象进行了定义,则可以将基类对象赋给派生对象。
派生类对象可以赋给基类对象。
五.c++作用域与重定义,赋值兼容规则
1.作用域与重定义(同名隐藏)
当我们在派生类中定义一个同名函数的时候,编译器是将同名函数隐藏了,不管参数表是否相同。即不会构成函数重载,直接将基类函数覆盖。
那问什么构不成函数重载呢?
一定要注意,函数重载的条件是在同一个作用域中才会构成函数重载,而派生类和基类是两个类域,一定不会构成函数重载的。
2.继承与转换——赋值兼容规则(public继承为例)
基类和派生类的特殊关系:
1.派生类对象可以赋值给基类对象 ;基类对象不能赋值给派生类。
2.基类指针可以在不进行显示类型转换的情况下指向派生类对象 。
3.基类引用可以在不进行显示类型转换的情况下引用派生类对象,但是基类指针或引用只能用于调用基类的方法,不能用基类指针或引用调用派生类的成员及方法。
C++要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是个例外。但是这个例外是单向的,即仅仅不可以将基类对象和地址赋给派生类引用和指针。如果允许基类引用隐式的引用派生类对象,则可以使用基类引用为派生类对象调用基类的方法,因为派生类继承了基类的方法,所以这样不会出现问题。但是如果可以将基类对象赋给派生类引用,那么派生类引用能够为积类对象调用派生类方法,这样做会出现问题,例如:用基类对象调用派生类中新增的方法,是没有意义的,因为基类对象中根本没有派生类的新增方法。
3.友员与继承
友元关系不能继承, 也就是说基类友元不能访问子类私有和保护成员 。友元只是能访问指定类的私有和保护成员的自定义函数,不是被指定类的成员,自然不能继承。
应该注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
(4)友元可以访问类的私有成员。
(5)友元只能出现在类定义内部,友元声明可以在类中的任何地方,一般放在类定义的开始或结尾。
(6)友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。
(7)类必须将重载函数集中每一个希望设为友元的函数都声明为友元。
(8)友元关系不能继承,基类的友元对派生类的成员没有特殊的访问权限。如果基类被授予友元关系,则只有基类具有特殊的访问权限。该基类的派生类不能访问授予友元关系的类。
4.继承与静态成员
基类定义了 static成员 , 则整个继承体系里面只 有一个这样的成员 。 无论派生出多少个子类, 都只有 一个static成员 实例。父类的static变量和函数在派生类中依然可用,但是受访问性控制(比如,父类的private域中的就不可访问)。而且对static变量来说,派生类和父类中的static变量是共用空间的,这点在利用static变量进行引用计数的时候要特别注意。派生类的friend函数可以访问派生类本身的一切变量,包括从父类继承下来的protected域中的变量。但是对父类来说,他并不是friend的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值