C++学习:类继承

这篇具有很好参考价值的文章主要介绍了C++学习:类继承。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

面相对象的主要目的之一就是提供可重用的代码。

类继承就是从已有的类派生出新的类,而派生类继承了原有类,也就是基类的特征和成员函数。

继承一笔财富比自己白手起家要简单的多,写代码也是一样。

下面是可通过继承来外城的工作:

1、可以在已有类的基础上添加新功能,例如,对于数组类,可以添加数学运算;

2、可以给类添加数据,例如,对于数据串类,可以派生出一个类,并添加指定字符串显示颜色的数据成员;

3、可以修改类成员函数的行为,例如给飞机乘客提供服务的Passenger类,可以派生出提供头等舱服务FirstClassPassenger类。

一个简单的基类

从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。

webtown俱乐部决定统计乒乓球会会员,俱乐部的程序员需要设计一个简单的TableTennisPlayer类

#ifndef TABLETENNISPLAYER_H
#define TABLETENNISPLAYER_H

#include <iostream>
#include <string>

using namespace  std;


class TableTennisPlayer
{
private:
    string firstname;
    string lastname;
    bool hasTable;
    
public:
    TableTennisPlayer(const string& fn = "none", const string& ln = "none", bool ht = false);
    void Name() const;
    bool HasTable() const {return hasTable;};
    void ResetTable(bool v) {hasTable = v;};
};

#endif // TABLETENNISPLAYER_H

 

#include "tabletennisplayer.h"

TableTennisPlayer::TableTennisPlayer(const string& fn , const string& ln, bool ht ):firstname(fn),lastname(ln),hasTable(ht)
{

}

void TableTennisPlayer::Name() const
{
   cout << lastname << ", " << firstname;                                      
}
    

 TableTennisPlayer类只是记录会员的姓名以及是否有球桌;

这个类是使用string类来存储姓名,比字符串数组更方便,更灵活,更安全;

构造函数使用了成员初始化列表语法,与下面的写法等效。

TableTennisPlayer::TableTennisPlayer(const string& fn , const string& ln, bool ht )
{
    firstname = fn;
    lastname = ln;
    hasTable = ht;
}

 使用前面的类

#include <iostream>
#include "tabletennisplayer.h"


int main()
{
    TableTennisPlayer player1("Chuck", "Blizzard", true);
    TableTennisPlayer player2("Tara", "Boomdea", false);
    player1.Name();
    if(player1.HasTable())
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    player2.Name();
    if(player2.HasTable())
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    return 0;
}

输出结果

Blizzard, Chuck: has a table.
Boomdea, Tara: hasn't a table.

 派生一个类

webtown俱乐部的一些成员曾经参加过当地的乒乓球竞标赛,需要这样一个类能包含成员在比赛中的比分。与其从0开始,不如从TableTennisPlayer类派生出来一个类。首先将RatedPlayer类声明为从TableTennisPlayer类派生而来

//声明一个派生类
//冒号:前面的是派生类,冒号后面的是基类
//TableTennisPlayer前面的public说明它是一个公有基类
//基类的私有部分称为派生类的一部分,只能通过基类的公有和保护方法去访问
class RatedPlayer : public TableTennisPlayer
{
private:
    unsigned int rating;
public:
    RatedPlayer(unsigned int r = 0, const string& fn = "none", const string& ln = "none", bool ht = false);
    RatedPlayer(unsigned int r, const TableTennisPlayer& tp);
    unsigned int Rating() const {return rating;};
    void ResetRating(unsigned int r) {rating = r;};
};

 上面的代码完成了如下工作:

1、派生类对象存储了基类的数据成员,也就是说派生类继承了基类的实现;

2、派生类对象可以使用基类的方法,也就是成员函数,也叫派生类继承了基类的接口;

因此RatedPlayer对象可以存储运动员的姓名、以及是否有球桌,另外RatedPlayer对象还可以使用TableTennisPlayer类的name()、hasTable()、ResetTable()函数。

需要再继承特性中添加什么?

1、派生类需要自己的构造函数;

2、派生类可以根据需要添加额外的成员和成员函数;

构造函数必须给次年成员和继承的成员提供数据。

构造函数的访问权限:

派生类不能直接访问基类的私有成员,必须通过基类的成员函数进行访问;例如,RatedPlayer构造函数不能直接设置集成的成员,如firstname、lastname、hasTable,必须使用基类的公有方法来访问私有的基类成员,也就是说派生类构造函数必须使用基类的构造函数。

创建派生类对象时,程序首先创建基类独享,这意味着基类对象应在程序进入派生类构造函数之前被创建。C++使用成员初始化列表语法来完成这种工作。

RatedPlayer::RatedPlayer(unsigned int r, const string& fn, const string& ln, bool ht):TableTennisPlayer(fn, ln, ht)
{
    rating = r;
}
:TableTennisPlayer(fn, ln, ht)是成员初始化列表,是可执行代码,调用TableTennisPlayer构造函数

派生类构造函数的注意事项:

1、首先创建基类对象;

2、派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;

3、派生类构造函数应初始化派生类新增的数据成员

使用派生类

#ifndef TABLETENNISPLAYER_H
#define TABLETENNISPLAYER_H

#include <iostream>
#include <string>

using namespace  std;

//声明一个基类
class TableTennisPlayer
{
private:
    string firstname;
    string lastname;
    bool hasTable;

public:
    TableTennisPlayer(const string& fn = "none", const string& ln = "none", bool ht = false);
    void Name() const;
    bool HasTable() const {return hasTable;};
    void ResetTable(bool v) {hasTable = v;};
};


//声明一个派生类
//冒号:前面的是派生类,冒号后面的是基类
//TableTennisPlayer前面的public说明它是一个公有基类
//基类的私有部分称为派生类的一部分,只能通过基类的公有和保护方法去访问
class RatedPlayer : public TableTennisPlayer
{
private:
    unsigned int rating;
public:
    RatedPlayer(unsigned int r = 0, const string& fn = "none", const string& ln = "none", bool ht = false);
    RatedPlayer(unsigned int r, const TableTennisPlayer& tp);
    unsigned int Rating() const {return rating;};
    void ResetRating(unsigned int r) {rating = r;};
};

#endif // TABLETENNISPLAYER_H

 

#include "tabletennisplayer.h"

TableTennisPlayer::TableTennisPlayer(const string& fn , const string& ln, bool ht ):firstname(fn),lastname(ln),hasTable(ht)
{

}

void TableTennisPlayer::Name() const
{
   cout << lastname << ", " << firstname;
}

RatedPlayer::RatedPlayer(unsigned int r, const string& fn, const string& ln, bool ht):TableTennisPlayer(fn, ln, ht)
{
    rating = r;
}

RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp):TableTennisPlayer(tp), rating(r)
{

}

 

#include <iostream>
#include "tabletennisplayer.h"


int main()
{
    TableTennisPlayer player1("Chuck", "Blizzard", true);
    RatedPlayer rplayer1(1140,"Tara", "Boomdea", false);
    player1.Name();
    if(player1.HasTable())
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    rplayer1.Name();
    if(rplayer1.HasTable())
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    cout << "name: ";
    rplayer1.Name();
    cout << " ; Rating: " << rplayer1.Rating() << endl;

    RatedPlayer rplayer2(1112, player1);
    cout << "name: ";
    rplayer2.Name();
    cout << " ; Rating: " << rplayer2.Rating() << endl;

    return 0;
}

输出结果

Blizzard, Chuck: has a table.
Boomdea, Tara: hasn't a table.
name: Boomdea, Tara ; Rating: 1140
name: Blizzard, Chuck ; Rating: 1112

 派生类与基类之间的特殊关系

1、派生类的对象可以使用基类的public成员函数,

RatedPlayer rplayer1(1140,"Tara", "Boomdea", false);
rplayer1.Name();

2、基类指针可以在不进行显式类型转换的情况下指向派生类对象;基类引用可以在不进行显式类型转换的情况下引用派生类对象;

TableTennisPlayer& rt = rplayer1;
TableTennisPlayer* pt = &rplayer2;
    
rt.Name();
pt->Name();

 但是基类指针只能用于调用基类成员函数,因此不能使用rt,pt来调用派生类ResetRating函数。

3、不能将基类的对象和指针赋给派生类的引用和指针;

多态公有继承

1、在派生类中重新定义基类的方法(成员函数);

2、使用虚函数

银行不同支票类

#ifndef BRASS_H
#define BRASS_H

#include <string>

using namespace std;


//基类Brass
//virtual 表示虚函数
class Brass
{
private:
    string fullName;
    long acctNum;
    double balance;

public:
    Brass(const string& s = "Nullboby", long an = -1, double bal = 0.0);
    void Deposit(double amt);
    virtual void Withdrow(double amt);
    double Balance() const;
    virtual void ViewAcct() const;
    virtual ~Brass() {};
};


//BrassPlus类在Brass类的基础上增加了三个私有数据成员和3个公有成员函数
//BrassPlus类与Brass类都声明了ViewAcct()和Withdrow()函数,但是它们的功能是不一样的
class BrassPlus : public Brass
{
private:
    double maxLoan;
    double rate;
    double owesBank;
public:
    BrassPlus(const string& s = "Nullbody", long an = -1, double ba = 0.0, double ml = 500, double r = 0.11125);

    BrassPlus(const Brass& ba, double ml = 500, double r = 0.11125);

    virtual void ViewAcct() const;
    virtual void Withdrow(double amt);
    void ResetMax(double m){maxLoan = m;}
    void ResetRate(double r){rate = r;}
    void ResetOwes(){owesBank = 0;}

};

#endif // BRASS_H

 

#include <iostream>
#include "brass.h"

typedef std::ios_base::fmtflags     format;
typedef streamsize                  precis;

format setFormat();
void restore(format f, precis p);


Brass:: Brass(const string& s, long an, double bal)
{
    fullName    = s;
    acctNum     = an;
    balance     = bal;
}

//存钱
void Brass::Deposit(double amt)
{
    if(amt < 0)
        cout << "negative deposit not allowed; " << "deposit is cancelled.\n";
    else
        balance += amt;
}

//取钱
void Brass::Withdrow(double amt)
{
    format initialState = setFormat();
    precis prec = cout.precision(2);

    if(amt < 0)
        cout << "negative withdrow not allowed; " << "withdrow is cancelled.\n";
    else if (amt <= balance)
        balance -= amt;
    else
        cout << "withdrawal amount of $" << amt << " exceeds your balance.\n";

    restore(initialState, prec);
}

//查询账户余额
double Brass::Balance() const
{
    return balance;
}

void Brass::ViewAcct() const
{
    //format initialState = setFormat();
    //precis prec = cout.precision(2);

    cout << "Client: " << fullName << endl;
    cout << "Account Number: " << acctNum << endl;
    cout << "Balance: $" << balance << endl;

    //restore(initialState, prec);
}



BrassPlus::BrassPlus(const string& s, long an, double bal, double ml, double r):Brass(s, an, bal)
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

BrassPlus::BrassPlus(const Brass& ba, double ml, double r):Brass(ba)
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

void BrassPlus::ViewAcct() const
{
    format initialState = setFormat();
    precis prec = cout.precision(2);

    Brass::ViewAcct();
    //cout << "maximumloan: " << maxLoan << endl;
    cout << "maximumloan: " << maxLoan << endl;
    cout << "owed to bank " << owesBank << endl;

    cout.precision(3);
    cout << "Loan Rate: " << 100 * rate << "%\n";

    restore(initialState, prec);

}

void BrassPlus::Withdrow(double amt)
{
    format initialState = setFormat();
    precis prec = cout.precision(2);

    double bal = Balance();
    if(amt <= bal)
        Brass::Withdrow(amt);
    else if(amt <= bal + maxLoan - owesBank)
    {
        double advance = amt - bal;
        owesBank += advance * (1.0 + rate);
        cout << "Bank advance: $" << advance << endl;
        cout << "Finance change: $" << advance * rate << endl;

        Deposit(advance);
        Brass::Withdrow(amt);
    }
    else
        cout << "Credit limit exceeded. Transaction cancelled.\n";


    restore(initialState, prec);
}



format setFormat()
{
    return cout.setf(ios_base::fixed, ios_base::floatfield);
}

void restore(format f, precis p)
{
    cout.setf(f, ios_base::floatfield);
    cout.precision(p);
}
#include <iostream>
#include "brass.h"


int main()
{
    Brass Jiayin("Xu Jiayin", 381299, 4000.00);
    BrassPlus Nianyu("beiji Nianyu", 382288, 3000.00);

    Jiayin.ViewAcct();
    cout << endl;

    Nianyu.ViewAcct();
    cout << endl;

    cout << "Depositing $1000 into the Nianyu Account:\n";
    Nianyu.Deposit(1000.00);
    cout <<"new balance: $" << Nianyu.Balance() << endl;

    cout << "withdrawing $4200 from the Jiayin Account:\n";
    Jiayin.Withdrow(4200.00);
    cout <<"Jiayin account balance: $" << Jiayin.Balance() << endl;


    cout << "withdrawing $4200 from the Nianyu Account:\n";
    Nianyu.Withdrow(4200.00);
    Nianyu.ViewAcct();

    return 0;
}

静态联编与动态联编

程序调用函数时,将使用哪个可执行块,是由编译器决定的。将源代码中的函数调用截石位执行特定的函数代码块的过程被称为函数名联编binding。在C语言中,这很简单,因为每个函数名都对应一个不同的函数。在C++中由于函数重载的缘故,这项任务更复杂。编译器必须查看函数参数和函数名才能确定使用哪个函数。但是编译器可以在编译过程中完成这种联编。在编译过程中进行联编就是静态联编。也叫早期联编。但是虚函数的存在,又加深了难度,在编译的时候是不知道该使用哪个函数的,所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编,也叫晚期联编。

有关虚函数的注意事项

1、在基类的成员函数的声明中,使用关键字virtual,可使该函数在基类以及所有的派生类中都是虚的;

2、如果使用指向对象的引用或指针来调用虚函数,程序将使用为对象类型定义的函数,而不是使用引用或指针定义的函数。这也称为动态联编;

3、如果定义的类被作为基类,则应将那些要在派生类中重新定义的成员函数声明为虚函数。

4、构造函数不能是虚函数;

5、析构函数应当是虚函数;文章来源地址https://www.toymoban.com/news/detail-719728.html

到了这里,关于C++学习:类继承的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 1024程序员节带你玩转图片Exif信息获取之JavaScript

    目录 一、前言 二、背景 三、Exif.js          1、Exif.js 简介 2、Exif.js 引入 四、多场景展示数据获取 1、原始图片直接获取  2、base64 编码文件加载  3、文件上传的方式加载  五、总结        1024是2的十次方,二进制计数的基本计量单位之一。1G=1024M,而1G与1级谐音,也有一

    2024年02月20日
    浏览(56)
  • 1024程序员节特辑 | Spring Boot实战 之 MongoDB分片或复制集操作

    Spring实战系列文章: Spring实战 | Spring AOP核心秘笈之葵花宝典 Spring实战 | Spring IOC不能说的秘密? 国庆中秋特辑系列文章: 国庆中秋特辑(八)Spring Boot项目如何使用JPA 国庆中秋特辑(七)Java软件工程师常见20道编程面试题 国庆中秋特辑(六)大学生常见30道宝藏编程面试题

    2024年02月08日
    浏览(76)
  • 1024程序员节特辑 | ELK+ 用户画像构建个性化推荐引擎,智能实现“千人千面”

    专栏集锦,大佬们可以收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https://blog.csdn.net/superdangbo/category_9271502.html tensorflow专栏:https://blog.csdn.net/superdangbo/category_869

    2024年02月07日
    浏览(80)
  • 1024程序员狂欢节 | IT前沿技术、人工智能、数据挖掘、网络空间安全技术

    一年一度的1024程序员狂欢节又到啦!成为更卓越的自己,坚持阅读和学习,别给自己留遗憾,行动起来吧! 那么,都有哪些好书值得入手呢?小编为大家整理了前沿技术、人工智能、集成电路科学与芯片技术、新一代信息与通信技术、网络空间安全技术,四大热点领域近期

    2024年02月06日
    浏览(64)
  • 程序员行业还是高薪职业吗?我来和大家聊聊C++程序员该如何学习

    此外,程序员的劳动大多是脑力活动,不需要东奔西跑。这也就意味着,程序员的工作不会对身体健康造成太大的影响。 我们都知道,我们现在的生活水平越来越高科技,越来越先进。在这样的发展速度下,程序员怎么可能被淘汰呢?所以,别听网上的瞎说,什么互联网红利

    2024年02月05日
    浏览(52)
  • 1024程序员节特辑 | 解密Spring Cloud Hystrix熔断提高系统的可用性和容错能力

    专栏集锦,大佬们可以收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https://blog.csdn.net/superdangbo/category_9271502.html tensorflow专栏:https://blog.csdn.net/superdangbo/category_869

    2024年02月08日
    浏览(50)
  • 1024程序员节?我们整点AI绘图玩玩吧,一文教你配置stable-diffusion

    需提前准备:一台高性能的电脑(尤其是显存)、python、Git、梯子。 其实Github上有很多关于Stable diffusion的库,综合对比之后,我选取的是比较全面的AUTOMATIC1111这个,源码链接:Stable-diffusion(Github) 找到安装那块的教程,此教程以windows为例。 ps:如果你电脑上已经有了pyt

    2024年01月16日
    浏览(71)
  • PHP框架开发实践 | 1024 程序员节:通过index.php找到对应的controller是如何实现的

    🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于PHP专栏:PHP进阶实战教程。 🎉欢迎 👍点赞✍评论⭐收藏

    2024年02月08日
    浏览(67)
  • c++学习笔记-提高编程-模板(哔站-黑马程序员c++教学视频)

    通用的模具,提高代码复用性 不可以直接使用,只是一个框架;模板的通用性并不是万能的。 3.2.1 函数模板 函数模板的作用:建立一个通用函数,其函数返回值类型和参数类型可以不具体确定,用一个虚拟的类型来代表。 1)语法: templatetypename T//函数声明或定义 函数 temp

    2023年04月11日
    浏览(81)
  • C++学习day--11 程序员必备工具--github

    github 的重要性: 网络时代的程序员必备。 github 的作用: 1. 版本管理 2. 多人协作 3. 开源共享 常用方案: git+TortoiseGit+github [Tortoise ,程序员常称其为小乌龟,小海龟 ] 安装配置步骤 1. 注册 https://github.com/ 使用邮箱: (例如:1374784346@qq.com) 密码需要至少包含一位小写字母,至

    2024年02月05日
    浏览(100)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包