继承是面向对象的一种很重要的特性,先来复习基类的基本知识:

先上一段代码:

 # ifndef  TABLE00_H
# define TABLE00_H
# include "string";
using std::string;
class Player
{
private:
string first_name;
string last_name;
bool SEAT;
public: //注意,这里只是头文件,进行函数声明的地方
//Player(const string & fn = "none", const string & ln = "none", bool symbol = false);
Player(const string & fn , const string & ln , bool symbol);
//注意,在最开始设计类的时候,可能我们并没有注意到 要使用 &引用 和const,使用& 和const的契机是:
//1 使用& 是因为,初始化类型为 string类型或者c类型字符串,进行这种非基类数据类型复制的时候,都会耗费大量内存和时间,所以采用引用&
// 2 使用const 的契机: 因为这里采用了引用,这种做法是为了不改变被引用引用的对象。思考将引用符号去掉,是否还有加const的必要性?
void NameShow()const;
bool SeatVerify()const;
//思考:在函数名后面加const,是因为在写函数体之前就想好了函数的功能是否改变成员变量,如果函数的功能不改变成员变量,就添加const,
//说白了这是一种从顶层到底层的设计,我们明白了函数的功能不改变成员变量,所以为了防止写函数体的过程改变成员变量,我们加了一个const。
// 一般的 const加在谁的前面。就是用来修饰谁的,加在返回类型前面,就是修饰返回值,加在形参前面,即修饰形参,则加在函数名后面,是修饰函数体的,具体的也就是
//不改变类对象的成员值。即这种函数称之为常成员函数。
//思考:当函数代码比较短的时候,可否在头文件直接使用内联函数将函数体键入?
void SetSeat(bool); };
//以下为共有继承类声明部分
class RePlayer :public Player
{
private:
unsigned int ratio;
public:
RePlayer(unsigned int, const string & fn, const string & ln, bool symbol);
RePlayer(unsigned int, const Player & np);
int Ratio() const;
void InitialRatio(unsigned int);
}; # endif

先复习基本知识:

1  # ifndef TABLE00_H...# endif 表明:如果之前没有定义TABLE00_H段,则编译# ifndef TABLE00_H...# endif之间程序段,否则不编译,这 能够避免一个文件被多个重叠文件连续包含时报错,比如B头文件包含了A文件,C头文件包含了B文件和A文件,那么如果没加# ifndef TABLE00_H...# endif ,则会因为重复定义报错,因此在写头文件时,一律写上 # ifndef TABLE00_H...# endif可以避免程序的报错问题。

2  对一个类而言,构造函数是十分重要的一个环节,构造函数存在的意义是:让私有成员变量被初始化,我们应当始终注意这一初衷,只有这样,我们才能设计正确的形参。

3  我们应该注意引用&变量的使用契机,当传递的参数是复杂数据类型(比如类和c类型的字符串),由于巨大的内存开销和调度,采用引用的方式无疑是一种高效的方式

4  上述代码段17,18,35行的函数成为:常成员函数,在此,先声明函数声明结尾const的作用,使得程序体不能改变私有成员变量的值(否则报错),比如成员显示函数,可以使用常成员函数

上述代码28-37行为继承类的生命,从这个声明我们可以得到这样一些基本信息与结论:

1  继承类首先也是类,具有类的一般特性:包括私有成员、公有成员,以及构造函数。

2  观察继承类的构造函数。发现其构造函数同样服从:让私有成员变量被初始化.但继承类继承了基类,因此也要对基类的成员进行初始化,说白了,要对所有的成员进行初始化。

易错:

也许有人看了13,33,34行的代码,会发出这样的疑问:为何这里使用了引用变量却没有初始化,引用变量在定义变量时不是要进行初始化吗?

回答:我们在声明类,甚至在定义类的时候,本质工作是什么???本质工工作是:构造,构造一个数据类型,并不是在定义变量,只有我们在使用类(构造的数据类型)去定义对象的时候,我们才是真正的定义了一个变量,所以 定义类的过程,并不是定义变量的过程,所以并不必要对&进行初始化,说白了,此时的引用&只是一个空壳子,并不实际的分配内存,进行初始化这些功能。

进行了类声明之后,但成员函数还未得到定义,为此,给出类定义:

 # include "table00.h"
# include "iostream"
using std::string;
using std::cout;
using std::endl;
/*class Player //如果在函数体文件再声明class Player则会出现重定义的情况!!!,所以采用这种做法是错误的。
{
private:
string first_name;
string last_name;
bool SEAT;
public:
Player(const string & fn = "none", const string & ln = "none", bool symbol = false)
{
first_name = fn;
last_name = ln;
SEAT = symbol;
}
void NameShow()const //注意在函数体中,这个const也不能丢舎.
{
cout << first_name << "," << last_name << endl;
}
bool SeatVerify()const
{
return SEAT;
}
void SetSeat(bool change_seat)
{
SEAT = change_seat;
}
};*/
//验证上述写法和下述写法哪个更好。以及对于作用域有没有更好的表示方法。
//Player::Player(const string & fn = "none", const string & ln = "none", bool symbol = false)
Player::Player(const string & fn , const string & ln, bool symbol ) {
first_name = fn;
last_name = ln;
SEAT = symbol;
}
void Player:: NameShow()const //注意在函数体中,这个const也不能丢舎.
{
cout << first_name << "," << last_name << endl;
}
bool Player:: SeatVerify()const
{
return SEAT;
}
void Player:: SetSeat(bool change_seat)
{
SEAT = change_seat;
}
//要认识到面向对象这个词的含义:函数的作用尽管也是为了完成一个功能,但更多的是完成对数据的操作,即我们更关注数据本身
// 成员函数的本质在于:服务于成员变量(通常情况是这样),所以在进行成员函数设计的时候,我们所关注的重点是:对成员变量进行何种操作,完成何种功能
//一定要注意主体对象是成员变量。 RePlayer::RePlayer(unsigned int v, const string & fn, const string & ln, bool symbol) : Player(fn, ln, symbol)
{
ratio = v;
// first_name = fn; 注意,如果我们试图直接访问基类私有变量,是有问题的
// last_name = ln; 但我们需要在调用继承类构造函数之前,调用基类构造函数。
// SEAT = symbol;
}
//这两条都是继承类构造函数,需要在调用之前调用基类构造函数,因此需要先初始化基类构造函数。
RePlayer::RePlayer(unsigned int v, const Player & np) : Player(np)
{ //需要注意的是:如果 前面定义 unsigned int v =0;则后面的np也要赋初值
//注意,这里的写法发生了重定义。
ratio = v;
// first_name = fn; 注意,如果我们试图直接访问基类私有变量,是有问题的
// last_name = ln; 但我们需要在调用继承类构造函数之前,调用基类构造函数。
// SEAT = symbol;
}
int RePlayer:: Ratio()const
{
return ratio;
} void RePlayer::InitialRatio(unsigned int initial)
{
ratio = initial;
}

关于成员函数(也被称为接口,其实很形象!!!)有以下内容需要说明:

1  无论是基类的成员函数,还是继承类的成员函数,发现:成员函数都更侧重于:对成员变量(也称为实现,也很形象)进行了何种操作。虽然成员函数也描述了:完成了一个怎样的功能,但我们更侧重于:对成员变量完成了一种怎样的功能,也就是最终落脚点在于:成员变量发生了什么?因此,我们在写成员函数的时候,一定不能漫无目的,思考要完成一个什么功能但脱离了成员变量,一定要认识到我们的成员函数是紧紧的围绕成员变量展开的。

2 关注继承类的构造函数的实现:也就是上述,57和65行的代码。在初始化一个继承类成员(实际上包含了基类成员在内的所有成员)的时候,必然先初始化基类的成员变量,要调用继承类的构造函数,一定要首先调用其基类的构造函数,完成对基类成员变量先进行初始化。因此在进入继承类构造函数函数体之前,必然先要调用基类构造函数完成基类成员变量的初始化。

这也是为什么57行Player(fn, ln, symbol)与65行的 Player(np)会写在函数体{}的前面

3  我们注意:60行和69行的代码,当我们试图去直接访问基类私有成员变量时,程序是禁止的,也就是说,我们只能通过基类的公有函数才能访问基类的私有成员。这一点保证了父类和子类的独立性关系。

最终,我们给出函数的调用:

 # include "table00.h"
# include "iostream"
using namespace std;
int main()
{
Player player1("jack", "cracy", true);
player1.NameShow();
Player player2(player1);
player2.NameShow();
RePlayer player3(, "robert", "lin", true);
player3.NameShow();
RePlayer player4(,player2);
player4.NameShow();
system("pause");
return ; }

从代码中,可以看到:继承类可以调用基类的公有函数。

上说代码体现了类的基本思想和类继承的基本思想,下面我们给出一个更深入的探讨,来探讨基类和继承类的一些关系:

 # include "table00.h"
# include "iostream"
using namespace std;
void Show(const Player & );
int main()
{
Player player1("jack", "cracy", true);
player1.NameShow();
Player player2(player1);
player2.NameShow();
RePlayer player3(, "robert", "lin", true);
player3.NameShow();
RePlayer player4(,player2);
player4.NameShow();
Player & p = player3; //我们可以用子类去初始化父类,这是没有问题的,因为子类继承了父类的特性(成员)
p.NameShow();
Player* q= & player4;
q->NameShow(); //注意指针的访问方式和引用访问方式的区别,
//引用就相当于是别名,所以引用名就和对象名是等价的,因此可以用.访问,而指针并不等价于别名
//指针的访问要采用->。
Player player("ma", "jack", false);
//RePlayer & rt = player;//我们不可以用父类来初始化(或者赋值)子类,因为子类具有父类不具备的一些特性(子类新3定义成员),
//RePlayer * pt = &player;
Player player5("li", "zhou", false);
Show(player5);//将基类对象作为实参 传递给 基类引用,可行不报错
RePlayer player6(, "lin", "wu", true);
Show(player6);//将继承类对象作为实参 传递给 基类引用,,可行不报错
player6=player5;//试图用基类对象 赋值 继承类 对象,报错!!!
player5 = player6;//用继承类对象 赋值 基类 对象,不报错,实际上,这里使用了运算符重载!!!player& operator=(const player & )const;
Player player7(player5); //用基类对象初始化另一个基类对象,可行
Player player8(player6);//用 继承类 对象初始化 基类对象,可行。
RePlayer player9(player6);// 用 继承类 对象 初始化 继承类对象 ,可行!
RePlayer player9(player5);//用 基类对象 初始化 继承类对象,不可行!!!
system("pause");
return ; } void Show(const Player& rt)
{
rt.NameShow();
}

上述代码体现了子类和父类这样的一些特性:

当进行类似于赋值操作的时候,子类可以对父类进行赋值,因为子类继承了父类的全部特性。但不能用父类对子类赋值,因为子类有的特性,父类不一定有。

同时,注意15,17行代码的给出了引用和指针的一些区别:引用可以看做是别名,但指针并不能看成别名。因此在访问的时候是有一定区别的。

c++入门之类继承初步的更多相关文章

  1. Kotlin入门(14)继承的那些事儿

    上一篇文章介绍了类对成员的声明方式与使用过程,从而初步了解了类的成员及其运用.不过早在<Kotlin入门(12)类的概貌与构造>中,提到MainActivity继承自AppCompatAc ...

  2. JavaScript面向对象轻松入门之继承(demo by ES5、ES6)

    继承是面向对象很重要的一个概念,分为接口继承和实现继承,接口继承即为继承某个对象的方法,实现继承即为继承某个对象的属性.JavvaScript通过原型链来实现接口继承.call()或apply()来实 ...

  3. React Native基础&入门教程:初步使用Flexbox布局

    在上篇中,笔者分享了部分安装并调试React Native应用过程里的一点经验,如果还没有看过的同学请点击<React Native基础&入门教程:调试React Native应用的一小 ...

  4. python入门18 继承和多态

    继承:子类继承父类的属性和方法  class 子类名(父类1,父类2) 多态:子类重写父类的方法 判断对象是否是某个类的实例:  isinstance(object,class) 父类-汽车类 #co ...

  5. php类知识点滴---类的实例化,构造函数,继承初步

    实例化类----黑科技用法,通过字符串来实例化 class coach { public function __construct() { echo "欢迎光临北武堂"." ...

  6. linux入门教程(四) 初步进入linux世界

    [Linux 系统启动过程] Linux的启动其实和windows的启动过程很类似,不过windows我们是无法看到启动信息的,而linux启动时我们会看到许多启动信息,例如某个服务是否启动. Lin ...

  7. c++入门之const初步理解

    关于const,首先建立这样的一个认识:const并不是定义了一个常量,而是定义了在某种环境下只读的变量.下面我们来区分一些东西: ; const int*p = &num; *p = ; i ...

  8. RxJS 入门指引和初步应用

    作者:徐飞链接:https://zhuanlan.zhihu.com/p/25383159来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. RxJS是一个强大的React ...

  9. 一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍

    我们在前一篇文章微软新神器-Power BI,一个简单易用,还用得起的BI产品中,我们初步介绍了Power BI的基本知识.由于Power BI是去年开始微软新发布的一个产品,虽然已经可以企业级应用, ...

随机推荐

  1. 前后端分离djangorestframework——视图组件

    CBV与FBV CBV之前说过就是在view.py里写视图类,在序列化时用过,FBV就是常用的视图函数,两者的功能都可以实现功能,但是在restful规范方面的话,CBV更方便,FBV还要用reque ...

  2. c# 设置MdiClient窗体的背景图片

    在窗体的InitializeComponent();方法后面添加下面的代码. MdiClient MC = new MdiClient(); MC.Name = "MdiClientForm ...

  3. Windows Server 2016-管理Active Directory复制任务

    Repadmin.exe可帮助管理员诊断运行Microsoft Windows操作系统的域控制器之间的Active Directory复制问题. Repadmin.exe内置于Windows Serv ...

  4. 百度地图和高德地图结合在web中的使用(二)

    百度地图在web中的使用(二) 背景:在做一个关于地理位置字段时,初始位置使用百度地图获取时失败,获取的位置信息不准确,奈何产品说友商好使的啊,F12看后是采用的高德,所以在这采用高德地图获取初始位置 ...

  5. 【算法】LeetCode算法题-Search Insert Position

    这是悦乐书的第152次更新,第154篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第11题(顺位题号是35).给定排序数组和目标值,如果找到目标,则返回索引. 如果没有, ...

  6. March 06th, 2018 Week 10th Tuesday

    Hope for the best, but prepare for the worst. 抱最好的愿望,做最坏的打算. To hope for the best and prepare for th ...

  7. Java strictfp

    strictfp关键字 用于强制Java中的浮点计算(float或double)的精度符合IEEE 754标准. 不使用strictfp:浮点精度取决于目标平台的硬件,即CPU的浮点处理能力. 使用s ...

  8. Teradata超长数据会截断

    1.数据库版本 Teradata 15.10 2.测试案例: create multiset table test_stg ( col1 ) CHARACTER SET LATIN not null ...

  9. Codeforces Round #546 (Div. 2) C. Nastya Is Transposing Matrices

    C. Nastya Is Transposing Matrices time limit per test 1 second memory limit per test 256 megabytes i ...

  10. Java面试知识点之线程篇(一)

    前言:在Java面试中,一定会遇到线程相关问题,因此笔者在这里总结Java中有关线程方面知识点,多数从网上得来(文中会贴出主要参考链接),有些也是笔者在面试中所遇到的问题,如有错误,请不吝指正.主要参 ...