09--c++ 类的继承与派生

c++ 类的继承与派生

一、基本概念

1、类的继承,是新的类从已有类那里得到已有的特性。或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。

2、派生类的声明:

class 派生类名:继承方式 基类名1, 继承方式 基类名2,...,继承方式 基类名n

{

派生类成员声明;

};

3、一个派生类可以同时有多个基类,这种情况称为多重继承,派生类只有一个基类,称为单继承。直接派生,间接派生。

4、继承方式规定了如何访问基类继承的成员。继承方式有public, private, protected。如果不显示给出继承方式,默认为private继承。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。

5、派生类继承基类中除构造和析构函数以外的所有成员。

6、派生类生成:

吸收基类成员(除构造析构函数以外的所有成员);

改造基类成员(根据继承方式调整基类成员的访问,函数在子类中的覆盖,以及虚函数在子类中的覆盖);

添加新的成员;

7、公有继承

当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可访问。即基类的公有成员和保护成员被继承到派生类中仍作为派生类的公有成员和保护成员。派生类的其他成员可以直接访问它们。无论派生类的成员还是派生类的对象都无法访问基类的私有成员。

8、私有继承

当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可访问。基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。无论是派生类的成员还是通过派生类的对象,都无法访问从基类继承的私有成员。通过多次私有继承后,对于基类的成员都会成为不可访问。因此私有继承比较少用。

9、保护继承

保护继承中,基类的公有成员和私有成员都以保护成员的身份出现在派生类中,而基类的私有成员不可访问。派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但是类外部通过派生类的对象无法访问它们,无论派生类的成员还是派生类的对象,都无法访问基类的私有成员。

二、派生类的构造函数和析构函数

1、派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,然后派生类中新增的成员在派生类的构造函数中初始化。

2、派生类构造函数的语法:

派生类名::派生类名(参数总表):基类名1(参数表1),基类名(参数名2)....基类名n(参数名n),内嵌子对象1(参数表1),内嵌子对象2(参数表2)....内嵌子对象n(参数表n)

{

派生类新增成员的初始化语句;

}

注:构造函数的初始化顺序并不以上面的顺序进行,而是根据声明的顺序初始化。

3、如果基类中没有不带参数的构造函数,那么在派生类的构造函数中必须调用基类构造函数,以初始化基类成员。

4、派生类构造函数执行的次序:

调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右);

调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序;

派生类的构造函数体中的内容。

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B1
{
public:
    B1(int i)
    {
        cout<<"constructing B1 "<<i<<endl;
    }
};

class B2
{
public:
    B2(int j)
    {
        cout<<"constructing B2 "<<j<<endl;
    }
};

class B3
{
public:
    B3()
    {
        cout<<"constructing B3"<<endl;
    }
};

class C: public B2, public B1, public B3
{
public:
    C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
    {

    }
private:
    B1 memberB1;
    B2 memberB2;
    B3 memberB3;
};

int main()
{
    C obj(1,2,3,4);

    return 0;
}

输出结果为:

constructing B2 2

constructing B1 1

constructing B3

constructing B1 3

constructing B2 4

constructing B3

5、析构函数

派生类的析构函数的功能是在该对象消亡之前进行一些必要的清理工作,析构函数没有类型,也没有参数。析构函数的执行顺序与构造函数相反。

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B1
{
public:
    B1(int i)
    {
        cout<<"constructing B1 "<<i<<endl;
    }
    ~B1()
    {
        cout<<"destructing B1"<<endl;
    }
};

class B2
{
public:
    B2(int j)
    {
        cout<<"constructing B2 "<<j<<endl;
    }
    ~B2()
    {
        cout<<"destructing B2"<<endl;
    }
};

class B3
{
public:
    B3()
    {
        cout<<"constructing B3"<<endl;
    }
    ~B3()
    {
        cout<<"destructing B3"<<endl;
    }
};

class C: public B2, public B1, public B3
{
public:
    C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
    {

    }
private:
    B1 memberB1;
    B2 memberB2;
    B3 memberB3;
};

int main()
{
    C obj(1,2,3,4);

    return 0;
}

输出结果为:

constructing B2 2

constructing B1 1

constructing B3

constructing B1 3

constructing B2 4

constructing B3

destructing B3

destructing B2

destructing B1

destructing B3

destructing B1

destructing B2

三、派生类成员的标识和访问

1、派生类成员属性划分为四种:

不可访问的成员;私有成员;保护成员;公有成员;

2、作用域分辨

形式为:基类名::成员名;基类名::成员名(参数表);

如果某派生类的多个基类拥有同名的成员,同时,派生类又新增这样的同名成员,在这种情况下,派生类成员将覆盖所有基类的同名成员。这就需要这样的调用方式才能调用基类的同名成员。

例子:多继承同名

#include <iostream>
#include <time.h>
using namespace std;

class B1
{
public:
    int nV;
    void fun()
    {
        cout<<"member of B1 "<<nV<<endl;
    }
};

class B2
{
public:
    int nV;
    void fun()
    {
        cout<<"member of B2 "<<nV<<endl;
    }
};

class D1: public B1, public B2
{
public:
    int nV;
    void fun()
    {
        cout<<"member of D1 "<<nV<<endl;
    }
};

int main()
{
    D1 d1;
    d1.nV = 1;
    d1.fun();
    d1.B1::nV = 2;
    d1.B1::fun();
    d1.B2::nV = 3;
    d1.B2::fun();

    return 0;
}

输出结果为:

member of D1 1

member of B1 2

member of B2 3

以上通过作用域分辨符,解决了访问基类中被屏蔽的同名成员。

3、如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,因此派生类中也就会产生同名现象,对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须用直接基类进行限定。

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B0
{
public:
    int nV;
    void fun()
    {
        cout<<"member of B0 "<<nV<<endl;
    }
};

class B1:public B0
{
public:
    int nV1;
};

class B2:public B0
{
public:
    int nV2;
};

class D1:public B1, public B2
{
public:
    int nVd;
    void fund()
    {
        cout<<"member of D1"<<endl;
    }
};

int main()
{
    D1 d1;
    d1.B1::nV = 2;
    d1.B1::fun();
    d1.B2::nV = 3;
    d1.B2::fun();

    return 0;
}

输出结果为:

member of B0 2

member of B0 3

在这种情况下,派生类对象在内存中就同时拥有成员nV及fun的两份拷贝。但是很多情况下,我们只需要这样一个这样的数据拷贝,同一成员的多份拷贝增加了内存的开销。可以通过虚函数来解决这个问题。

4、虚基类

为了解决前面提到的多重拷贝的问题,可以将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数也只有一个映射。

虚基类的声明是在派生类的声明过程,其语法形式为:

class 派生类名::virtual 继承方式 基类名;

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B0
{
public:
    int nV;
    void fun()
    {
        cout<<"member of B0 "<<nV<<endl;
    }
};

class B1:virtual public B0
{
public:
    int nV1;
};

class B2:virtual public B0
{
public:
    int nV2;
};

class D1:public B1, public B2
{
public:
    int nVd;
    void fund()
    {
        cout<<"member of D1"<<endl;
    }
};

int main()
{
    D1 d1;
    d1.nV = 2;
    d1.fun();

    return 0;
}

输出结果为:

member of B0 2

5、虚基类及其派生类的构造函数

一般而言,派生类只对其直接基类的构造函数传递参数,但是在虚基类中,不管是直接或间接虚基类的所有派生类,都必须在构造函数的成员初始化列表中列出对虚基类的初始化。

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B0
{
public:
    B0(int n)
    {
        nV = n;
    }
    int nV;
    void fun()
    {
        cout<<"member of B0 "<<nV<<endl;
    }
};

class B1:virtual public B0
{
public:
    B1(int a):B0(a)
    {
    }
    int nV1;
};

class B2:virtual public B0
{
public:
    B2(int a):B0(a)
    {
    }
    int nV2;
};

class D1:public B1, public B2
{
public:
    D1(int a):B0(a), B1(a), B2(a)
    {
    }
    int nVd;
    void fund()
    {
        cout<<"member of D1"<<endl;
    }
};

int main()
{
    D1 d1(1);
    d1.nV = 2;
    d1.fun();

    return 0;
}

以上例子看上去B0的构造函数好像被调用了三次,但是实际上只有D1类中的D1(int a):B0(a), B1(a), B2(a)
才是真正的调用了B0构造函数。

四、赋值兼容规则

1、赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。

2、赋值兼容规则中所指的替代包括:

派生类的对象可以赋值给基类对象;

派生类的对象可以初始化基类的引用;

派生类对象的地址可以赋给指向基类的指针。

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

例子:

#include <iostream>
#include <time.h>
using namespace std;

class B0
{
public:
    void display()
    {
        cout<<"B0::display()"<<endl;
    }
};

class B1:public B0
{
public:
    void display()
    {
        cout<<"B1::display()"<<endl;
    }
};

class B2:public B0
{
public:
    void display()
    {
        cout<<"B2::display()"<<endl;
    }
};

void fun(B0 *ptr)
{
    ptr->display();
}

int main()
{
    B0 b0;
    B1 b1;
    B2 b2;
    fun(&b0);
    b0 = b1;
    fun(&b0);
    b0 = b2;
    fun(&b0);

    return 0;
}

输出结果为:

B0::display()

B0::display()

B0::display()

通过这种赋值兼容后,每次调用的同名函数都是基类的同名函数,如果想调用派生类的,则需要使用虚函数。

时间: 2024-10-13 01:28:41

09--c++ 类的继承与派生的相关文章

模块的封装之C语言类的继承和派生

[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我们将 在此的基础上介绍C语言类的继承和派生.其实继承和派生是一个动作的两种不同角度的表达 .当我们继承了一个基类而创造了一个新类时,派生的概念就诞生了.派生当然是从基类派生的.派生出来的类当然是继承了基类的 东西.继承和派生不是一对好基友,他们根本就是一个动作的两种不同的说法,强调动作的起始点的时候

黑马程序员-----类的继承和派生概念

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ----- 第一讲 类的继承和派生概念 本小节知识点: 1.[理解]什么是继承? 2.[掌握]类的继承和派生概念 1.什么是继承? 2.OC中的继承与派生 1 #import <Foundation/Foundation.h> 2 3 @in

Day17:类的继承、派生、组合和接口

一.类的继承.派生和组合 继承是一种创建新类的方式,在Python中,新类可以继承一个或多个父类,父类又可称为基类或者超类,新建的类称为派生类或子类. 在Python3中,所有类都默认继承object,都是新式类.在Python2中,有经典类和新式类.没有继承object类以及object的子类的类都是经典类. 1.继承 Python中类的继承分为:单继承和多继承 class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass clas

类的继承和派生

一.类的继承.派生和组合 继承是一种创建新类的方式,在Python中,新类可以继承一个或多个父类,父类又可称为基类或者超类,新建的类称为派生类或子类. 在Python3中,所有类都默认继承object,都是新式类.在Python2中,有经典类和新式类.没有继承object类以及object的子类的类都是经典类. 1.继承 Python中类的继承分为:单继承和多继承 class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass clas

[C++]类的继承与派生

继承性是面向对象程序设计的第二大特性,它允许在既有类的基础上创建新类,新类可以继承既有类的数据成员和成员函数,可以添加自己特有的数据成员和成员函数,还可以对既有类中的成员函数重新定义.利用类的继承和派生实现了更高层次的代码可重用性,符合现代软件开发的思想. C++语言同时支持单一继承和多重继承.单一继承是指派生类只从一个基类继承而来:相应的,多重继承指派生类同时从两个或更多的基类继承而来.java只支持单一继承. 一. 派生类 派生类的定义格式如下: class <派生类名>:[继承方式]&l

Day29:类的继承、派生、组合和接口

一.类的继承.派生和组合 继承是一种创建新类的方式,在Python中,新类可以继承一个或多个父类,父类又可称为基类或者超类,新建的类称为派生类或子类. 在Python3中,所有类都默认继承object,都是新式类.在Python2中,有经典类和新式类.没有继承object类以及object的子类的类都是经典类. 1.继承 Python中类的继承分为:单继承和多继承 class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass clas

实验4 类的继承、派生和多态(1)

实验目的 1. 理解类的继承和派生机制 2. 掌握派生类的定义和使用 3. 理解和掌握派生类成员的标识和访问中同名覆盖原则.二元作用域分辨符和虚基类的用法 4. 掌握派生类构造函数和析构函数的定义及调用次序 5. 理解运算符重载的目的,掌握运算符重载函数的编写方法 实验准备 1. 类的继承和派生 引入继承和派生机制的目的 基本概念:继承.派生.基类.直接基类.间接基类 .派生类 语法 派生类定义的语法格式(单重继承和多重继承) 派生类构造函数及其初始化列表书写形式 派生类成员的标识与访问 同名覆

c++实验四 类的继承、派生和多态

实验目的 1. 理解类的继承和派生机制 2. 掌握派生类的定义和使用 3. 理解和掌握派生类成员的标识和访问中同名覆盖原则.二元作用域分辨符和虚基类的用法 4. 掌握派生类构造函数和析构函数的定义及调用次序 5. 理解运算符重载的目的,掌握运算符重载函数的编写方法 实验内容 一.ElectricCar类 #ifndef BATTERY_H #define BATTERY_H class Battery { public: Battery(int nb = 70); int showbattery

类的继承与派生

继承与派生 在教材中给出的对类的继承和派生的解释: "类的继承,是新的类从已有类那里得到已有的特性." "从已有的类产生新类的过程就是类的派生." 新的类是基类的派生类,新的类从基类处得到已有的特性. 一.继承与派生举例 新的类是基类的派生类,新的类从基类处得到已有的特性. 例1.写一个类student,再写一个Student类的派生类Undergraduate. 代码: #define _CRT_SECURE_NOWARNINGS #include<iost