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

先上一段代码:

 # 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. shell编程—简单的使用(二)

    使用shell编辑.sh使其输出hello tynam 1.新建一个.sh文件,然后进行编辑 vi hello_tynam.sh 2.进行编辑,先按i键进行激活,然后输入echo hello tyna ...

  2. CentOS7中启动Tomcat后,8080端口不能被外部访问的解决办法。

    运行:/sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT

  3. Windows Server 2016-域站点复制查询

    了解了有关站点复制概念性内容后,后续几章节我们会围绕站点复制相关内容对域控的日常复制.维护等进行简单介绍.本章为大家带来有关域控站点复制查询的相关内容,希望大家可以喜欢.站点内域控制器之间的复制拓扑由 ...

  4. This network connection does not exist

    This network connection does not exist 在windows server 2008上面map了一个磁盘,共享的folder被我停止共享后,点击该磁盘的disconn ...

  5. jenkins 构建到最后报权限的问题

    参考链接  https://blog.csdn.net/sinat_25306771/article/details/54633921 近整理虚拟机的密码  把Jenkins构建相关的远程执行脚本的服 ...

  6. M码小黄衫买家秀=w=

    M码小黄衫买家秀=w= 17°的天气穿不了短袖polo..就只能这样强行上图啦~ 因为我一直耿耿于大一面向对象课上拿到的那件XL码小黄衫,长到能穿到膝盖,拍小黄衫全家福时候只能很凄凉的借了件小号的穿, ...

  7. 《Java大学教程》—第6章 类和对象

    6.2 对象:结构化编程-->数据-->封装(聚合,信息隐藏)-->对象(方法及其操作的数据都聚合在一个单元中,作为更高层的组织单元)-->类(创建对象的模板)6.3 类:*  ...

  8. ES5-ES6-ES7_Promise对象详解

    Promise对象概述(什么是Promise) Promise 是异步编程的一种解决方案,比传统的异步解决方案——回调函数和事件——更合理和更强大 所谓Promise,简单说就是一个容器,里面保存着某 ...

  9. Java学习笔记(三)——封装、继承、多态

    一.封装 概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问. 实现步骤: 修改属性的可见性——设为private. 创建getter/se ...

  10. vue methods 中方法的相互调用

    vue在同一个组件内:方法之间经常需要互相调用. methods中的一个方法如何调用methods中的另外一个方法呢? 可以在调用的时候使用  this.$options.methods.test2( ...