C++中的继承和派生

本文深入探讨C++中的继承与派生概念,解析单继承与多继承的构造、析构过程,以及公有、私有、保护继承的特点。通过实例演示派生类如何复用基类代码,实现代码重用和功能扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**

继承与派生概述
继承与派生是同一过程从不同的角度看
保持已有类的特性而构造新类的过程称为继承
在已有类的基础上新增自己的特性而产生新类的过程称为派生。
被继承的已有类称为基类(或父类)   base class
派生出的新类称为派生类(或子类)   derived class

继承的目的:实现设计与代码的重用。(充分利用原来已经有的类和代码)
派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。

单继承时派生类的定义
单继承是指派生类直接从一个基类继承
语法
    class 派生类名:继承方式  基类名
    {
        成员声明;
    }
继承方式一般使用public,很少情况下使用private

派生类的构成
    吸收基类成员
        在默认情况下,基类的构造函数和析构函数时不被继承到派生类中的
        默认情况下,派生类包含了全部基类中除了构造函数和析构函数之外的所有成员
    改造基类成员
        如果派生类声明了一个劲儿某基类成员同名的新成员,派生的新成员就会隐藏或者覆盖了外层的同名成员
    添加新的成员
        在派生类中添加新的数据成员和函数成员,以实现新的

**/

/**
继承方式简介及公有继承
不同继承方式的影响主要体现在:
派生类成员对基类成员的访问权限
    在派生类的内部,派生类的成员对于原始的基类成员的访问权限:
    派生类内部的成员不能访问基类的private成员,只能通过基类的公有成员接口访问基类的私有成员
    派生类内部的成员将基类的public和protected成员直接继承为公有成员,即在派生类内部可以直接访问基类的public成员
通过派生类对象对基类成员的访问权限
    在派生类和基类之外构造派生类的对象,通过对象只能够访问派生类和基类的public成员,而对于派生类和基类的
    proteced和private成员,对象均不能访问(这一点与类的对象完全相同)
三种继承方式
    公有继承
    私有继承
    保护继承
公有继承(public)   程序中最常用的继承方式
    继承的访问控制
    基类的public和protected成员:访问属性在派生类中保持不变;
    基类的private成员:不可直接访问。
    (派生类的还曾原函数不能直接访问从基类继承过来的私有数据成员,只能通过基类为私有成员提供的公有访问接口访问私有成员)
访问权限
    派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
    通过派生类的对象:只能访问public成员。
**/
#include<iostream>
using namespace std;

class Point{//基类point类的定义
public:// 公有函数成员
    void initPoint(float x=0,float y=0){this->x=x;this->y=y;}
    void move(float xOff,float yOff){x+=xOff;y+=yOff;}
    float getX(){return x;}
    float getY(){return y;}
private:
    float x;
    float y;
};

class Rectange:public Point{
    // 派生类定义的部分
public:
    // 新增的公有函数成员
    void initRectange(float x,float y,float h,float w){
        initPoint(x,y);// 调用基类的公有成员函数
        this->w=w;
        this->h=h;
    }
    float getW(){return w;}
    float getH(){return h;}
private:
    // 新增的私有数据成员
    float w;
    float h;
};

int main(){
    Rectange rec;
    rec.initRectange(2,3,20,20);
    rec.move(3,2);
    cout<<rec.getH()<<endl;
    cout<<rec.getW()<<endl;
    cout<<rec.getX()<<endl;
    cout<<rec.getY()<<endl;
    /** 当派生类继承了基类后,基类的公有成员函数就完全相当于派生类的公有成员函数,
    在派生类的类內或者由派生类产生的对象,都对基类的公有成员函数具有直接访问的权限 **/
    return 0;
}

/**
公有派生类对象可以被当作基类的对象使用,反之则不可。
派生类的对象可以隐含转换为基类对象;
派生类的对象可以初始化基类的引用;
派生类的指针可以隐含转换为基类的指针。
在将派生类转换成基类的类型后,通过基类对象名、指针只能使用从基类继承的成员。
公有派生使得基类的对外访问接口是保持不变的
**/

/**
派生类的构造和析构

默认情况下:基类的构造函数不被继承;派生类需要定义自己的构造函数
派生类的构造函数需要向基类的构造函数传递参数,
即将从基类继承过来的成员变量初始化所需要的参数传递给基类的构造函数
再由基类的构造函数完成对基类成员的初始化
由派生类构造函数的函数体以及初始化列表完成对派生类新增成员变量的构造(初始化)

若不继承基类的构造函数
    派生类新增成员:派生类定义构造函数初始化;
    继承来的成员:自动调用基类构造函数进行初始化;
    派生类的构造函数需要给基类的构造函数传递参数。

单继承(派生类只有一个直接基类)
    派生类的构造函数只需要给一个直接基类的构造函数传递参数即可

单继承时构造函数的定义语法
    派生类名::派生类名(基类所需的形参,本类成员所需的形参):
    基类名(参数表), 本类成员初始化列表  //  初始化列表
    {
        //其他初始化;
    };
单继承情况下派生类的构造函数,以及构造函数的调用过程
最好不要从基类直接继承构造函数,而是要在派生类的构造函数中给基类的构造函数传递初始化的参数
由C++的机制在调用派生类的构造函数时,自动调用基类的构造函数并将基类的初始化列表传递给基类的构造函数

构造函数的执行顺序
    调用基类构造函数。
    顺序按照它们被继承时声明的顺序(从左向右)。// 当派生类继承了多个基类时
    对初始化列表中的成员进行初始化。
    顺序按照它们在类中定义的顺序。
    对象成员初始化时自动调用其所属类的构造函数。由初始化列表提供参数。
    执行派生类的构造函数体中的内容。

派生类的构造函数对于各个基类和本类新增成员变量的初始化顺序
    首先按照派生类继承基类声明时的顺序,自动调用各个基类的构造函数
    再调用派生类的构造函数对派生类新增的成员数据进行初始化
派生类的析构函数执行顺序
    首先按照声明派生类各个基类的顺序的相反次序依次自动调用各个基类的析构函数
    再调用派生类的析构函数析构新增的成员变量
默认情况下,派生类不会自动继承基类的构造函数和析构函数,而是在程序执行到
派生类的构造函数或者析构函数时,自动调用基类的构造函数和析构函数
且构造和析构的顺序恰好相反,
派生类的构造函数是先构造各个基类部件,再构造新增成员变量
派生类的析构函数是先析构新增的成员变量,再析构各个基类部件
**/
#include<iostream>
using namespace std;

class Base{// 基类
public:
    Base();// 默认构造函数
    Base(int i):b(i){cout<<"正在调用Base类的构造函数..."<<endl;}
    ~Base(){cout<<"正在调用Base类的析构函数..."<<endl;}
    void print() const;// 由于print函数不改变类內成员变量的数值,故而定义成静态成员函数
private:
    int b;//私有成员变量
};

void Base::print() const{
    cout<<"Base 类中的数值 b= "<<b<<endl;
}

class C:public Base{// 类C为派生类,以公有继承的方式继承基类
public:
    C();// 无参数的默认构造函数
    ~C(){cout<<"正在调用C类的析构函数..."<<endl;}
    C(int b,int i):Base(b),c(i){cout<<"正在调用C类的构造函数..."<<endl;}
    void print() const;//静态成员函数
private:
    int c;
};
void C::print() const{
    cout<<"C 类中的数值 c= "<<c<<endl;
}
int main(){
    C obj(-10,5);
    obj.print();
    return 0;
}

/**
执行结果
正在调用C类的构造函数...
C 类中的数值 c= 5
正在调用C类的析构函数...
正在调用Base类的析构函数...
**/
/**
每个基类的复制构造函数负责本类对象成员的复制构造
从基类继承的成员的复制构造由基类的复制构造函数完成
派生类新增成员的复制构造由派生类的复制构造函数完成

派生类的复制构造函数
    派生类未定义复制构造函数的情况
    编译器会在需要时生成一个隐含的复制构造函数;
    先调用基类的复制构造函数;
    再为派生类新增的成员执行复制。
派生类定义了复制构造函数的情况
    一般都要为基类的复制构造函数传递参数。
    复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,
        也将被传递给基类的复制构造函数。
    基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用
    例如: C::C(const C &c1): B(c1) {…}
    在将派生类转换成基类的类型后,通过基类对象名、指针只能使用从基类继承的成员。
**/
#include<iostream>
using namespace std;

class Base{// 基类
public:
    Base();// 默认构造函数
    Base(int i):b(i){cout<<"正在调用Base类的构造函数..."<<endl;}
    Base(const Base &obj_base);
    ~Base(){cout<<"正在调用Base类的析构函数..."<<endl;}
    void print() const;// 由于print函数不改变类內成员变量的数值,故而定义成静态成员函数
private:
    int b;//私有成员变量
};
Base::Base(const Base &obj_base){
// 基类的复制构造函数   所有的复制构造函数都不要写返回值
    cout<<"正在调用Base类的复制构造函数..."<<endl;
    b=obj_base.b;
}

void Base::print() const{
    cout<<"Base 类中的数值 b= "<<b<<endl;
}

class C:public Base{// 类C为派生类,以公有继承的方式继承基类
public:
    C();// 无参数的默认构造函数
    C(int b,int i):Base(b),c(i){cout<<"正在调用C类的构造函数..."<<endl;}
    C(const C &obj);
    ~C(){cout<<"正在调用C类的析构函数..."<<endl;}
    void print() const;//静态成员函数
private:
    int c;
};
C::C(const C &obj):Base(obj){
    cout<<"正在调用C类的复制构造函数..."<<endl;
    c=obj.c;
};
void C::print() const{
    cout<<"C 类中的数值 c= "<<c<<endl;
}
int main(){
    C obj(-10,5);
    obj.print();
    C obj_copy(obj);
    obj_copy.print();
    return 0;
}
/**
程序输出
正在调用Base类的构造函数...
正在调用C类的构造函数...
C 类中的数值 c= 5
正在调用Base类的复制构造函数...
正在调用C类的复制构造函数...
C 类中的数值 c= 5
正在调用C类的析构函数...
正在调用Base类的析构函数...
正在调用C类的析构函数...
正在调用Base类的析构函数...
**/

派生类的析构函数
析构函数不被继承,派生类如果需要,要自行声明析构函数。
声明方法与无继承关系时类的析构函数相同。
不需要显式地调用基类的析构函数,系统会自动隐式调用。
先执行派生类析构函数的函数体,再调用基类的析构函数。

/**
访问从基类继承的成员
作用域限定
    当派生类与基类中有相同成员时:
    (1)若未特别限定,则通过派生类对象使用的是派生类中的同名成员(根据同名隐藏规则)
    (2)如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定。
**/
#include<iostream>
using namespace std;

class Base1{//定义基类Base1
public:
    Base1(){cout<<"正在调用Base1类的构造函数"<<endl;}
    ~Base1(){cout<<"正在调用Base1类的析构函数"<<endl;}

    int var;
    void fun(){cout<<"member of Base1"<<endl;}
};
class Base2{//定义基类Base2
public:
    Base2(){cout<<"正在调用Base2类的构造函数"<<endl;}
    ~Base2(){cout<<"正在调用Base2类的析构函数"<<endl;}

    int var;
    void fun(){cout<<"member of Base2"<<endl;}
};

class Derived:public Base2,public Base1{//定义派生类Derived
    /**
    当派生类同时继承了多个基类时,对于基类进行构造函数的顺序
    按照声明基类的顺序从前向后,即先构造Base2,再构造Base1,最后构造派生类的成员变量
    析构函数的调用顺序与构造函数的调用顺序完全相反,先析构派生类的成员变量,
    再按照声明基类的相反顺序,即先析构Base1,再析构Base2
    **/
public:
    Derived(){cout<<"正在调用Derived类的构造函数"<<endl;}
    ~Derived(){cout<<"正在调用Derived类的析构函数"<<endl;}
    int var;
    void fun(){cout<<"member of Derived"<<endl;}
    // 在派生类中定义了同名函数成员和同名变量成员,则在基类中的同名成员将会被隐藏
};

int main(){
    Derived d;// 初始化派生类对象
    Derived *p=&d; // 初始化指向派生类的指针,指针中所存储的地址就是类对象的起始地址


    d.Base1::fun();// 通过作用域限定符访问基类中与派生类同名的数据成员
    d.Base2::fun();
    d.fun();

    p->Base1::fun();

    return 0;
}
/**
程序运行结果
正在调用Base2类的构造函数
正在调用Base1类的构造函数
正在调用Derived类的构造函数
member of Base1
member of Base2
member of Derived
member of Base1
正在调用Derived类的析构函数
正在调用Base1类的析构函数
正在调用Base2类的析构函数

**/
/***
C7-1 账户类  (100/100 分数)
题目描述
定义一个基类Account,数据成员包含string类变量userName用于保存账户主人姓名,
函数成员包括默认构造函数、带参构造函数用于初始化数据成员和输出姓名的成员函PrintName()。
从Account类派生出CreditAccount类,增加整型数据成员credit用于记录该用户信用额度,
函数成员包括带参构造函数用于初始化数据成员和输出账户信息的成员函数PrintInfo()。
要求:在函数PrintInfo()中需要调用基类的成员函数PrintName()。填充以下代码:
**/
#include <iostream>
#include <string>
using namespace std;

class Account// 定义基类
{
     string userName;
     /**c++中,如果我们对类的成员(包括成员变量和成员函数)没有定义属性,则默认是private**/
public:
     Account(){};
     Account( string name );
     void  PrintUserName();
};
Account::Account( string name ){
    userName=name;
}
void Account::PrintUserName(){
    cout<<userName<<endl;
}

class CreditAccount : public Account// 定义派生类
{
public:
     CreditAccount( string name, long credit);
     void PrintInfo();
private:
     int credit;
};

CreditAccount::CreditAccount(string name, long number):Account(name){
    credit=static_cast<int>(number);
}
void CreditAccount::PrintInfo(){
    cout<<credit<<endl;
}

//请实现Account构造函数Account(char *name)
//请实现Account的PrintUserName()函数
//请实现CreditAccount类的构造函数CreditAccount(char* name, long number)
//请实现CreditAccount类的PrintInfo()函数

int main()
{
     CreditAccount a("I Love CPP", 10000);
     a.Account::PrintUserName();
     a.PrintInfo();
     return 0;
}
/**
C7-2 多继承  
题目描述
下面的代码声明了三个基类Base1、Base2和Base3,然后从这三个基类按照公有方式派生出类Derived。
在每个类中分别定义带一个整型参数的构造函数和析构函数输出提示信息,
构造函数的提示信息中需要包含整型参数的数值。
请将下面的代码补充完整,使得输出结果与样例输出相同,注意:测试数据有多组。
**/
#include <iostream>
using namespace std;

class Base1
{
public:
    Base1(int x);
    ~Base1();
};

class Base2
{
public:
    Base2(int x);
    ~Base2();
};
class Base3
{
public:
    Base3(int x);
    ~Base3();
};

class Derived:public Base2,public Base1,public Base3//继承上面3个类
{
public:
    Derived(int x1, int x2, int x3, int x4);
    ~Derived();
};
Derived::Derived(int x1, int x2, int x3, int x4):Base2(x3),Base1(x2),Base3(x4){
    cout<<"Derived constructor called "<<x1<<endl;
}
Derived::~Derived(){
    cout<<"Derived destructor called"<<endl;
}
Base1::Base1(int x)
{
    cout<<"Base1 constructor called "<<x<<endl;
}

Base1::~Base1()
{
    cout<<"Base1 destructor called"<<endl;
}
//依照Base1类中的代码实现其它类的构造函数和析构函数

Base2::Base2(int x)
{
    cout<<"Base2 constructor called "<<x<<endl;
}

Base2::~Base2()
{
    cout<<"Base2 destructor called"<<endl;
}

Base3::Base3(int x)
{
    cout<<"Base3 constructor called "<<x<<endl;
}

Base3::~Base3()
{
    cout<<"Base3 destructor called"<<endl;
}

int main()
{
    int x[4];
    for (int i = 0; i < 4; ++i)
        cin >> x[i];
    Derived d(x[0], x[1], x[2], x[3]);
    return 0;
}
/**
题目描述
下面的代码声明了两个基类Base1和Base2,然后从这两个基类按照公有方式派生出类Derived。
基类和派生类都各自包含一个公有成员x,并且Base1和Base2各有接受一个整型参数的构造函数,
Derived的构造函数接受Base1和Base2的对象引用a,b来初始化Derived类对象,
并令x为Base1::x和Base2::x之和。请将下面的代码补充完成,使得输出符合要求
**/
#include <iostream>
using namespace std;

class Base1{
public:
    int x;
    Base1(int x);
};

Base1::Base1(int x):x(x){;}

class Base2{
public:
    int x;
    Base2(int x);
};

Base2::Base2(int x):x(x){;}

class Derived:public Base1, public Base2{
public:
    int x;
    Derived(Base1& a, Base2& b);
};

Derived::Derived(Base1& a, Base2& b):Base1(a.x),Base2(b.x){
    x=a.x+b.x;
}

//请实现Base1,Base2, Derived的构造函数



int main()
{
    int x, y;
    cin >> x >> y;
    Base1 a(x);
    Base2 b(y);
    Derived d(a, b);
    cout << d.Base1::x << "+" << d.Base2::x << "=" << d.x << endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值