1、初识继承
执行代码
#include <iostream>
#include "pingpong.h"
int main()
{
std::cout << "Hello World!\n";
using std::cout;
pingpong player1("james", "hua", false);
pingpong player2("lucy", "ling", true);
//因为"james"与const string & 类型是不匹配的;
//但是会通过两种方式使之有效
//其一;将string作为pingpong的参数,将调用const string&作为参数的string 构造函数
//其二;将c-风格字符串作为 pingpong的参数,将调用const char*作为参数的string 构造函数
//二者取其一
return 0;
}
pingpong 类
#include <iostream>
#include <string>
using namespace std;
class pingpong
{
private:
string firstname;
string lastname;
bool hasTabel;
public:
pingpong(const string &fn = "none", const string &ln = "none",bool ht=false);
void SName()const;
bool HasTable()const { return hasTabel; };
void ResetTable(bool v) { hasTabel=v;};
};
//-----------------cpp的实现-----------------//
#include "pingpong.h"
pingpong::pingpong(const string &fn, const string &ln, bool ht):firstname(fn),lastname(ln),hasTabel(ht)
{//这也是继承
}
//和下面这个作用相同; 但是初始化,可以直接调用复制构造函数将 firstname 初始化为fn等等。
// 下面这个会多一步调用赋值运算符
//pingpong::pingpong(const string &fn, const string &ln, bool ht)
//{
// firstname=fn; lastname =ln; hasTabel= ht;
//}
void pingpong::SName() const
{
std::cout<<lastname << "," << firstname;
}
继承类RatedPlayer
#pragma once
#include"pingpong.h"
class RatedPlayer:public pingpong
{//: 指出它的基类是pingpong。ratedplay为派生类
//: 使用共有派生,基类的公有成员成为派生类的共有成员
//: 基类的私有部分,也会成为派生类的一部分;但是只能通过基类的共有和保护访问
//需要添加自己的构造函数,据需求添加数据成员等等
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 pingpong &pg);
unsigned int Rating()const { return rating; };
void ResetRating(unsigned int r) { rating = r; };
};
//-----------------cpp的实现-----------------//
#include "RatedPlayer.h"
RatedPlayer::RatedPlayer(unsigned int r, const string &fn, const string &ln, bool ht):pingpong(fn,ln,ht)
{//把形参传递给pingpong的构造函数
rating = r;
}
RatedPlayer::RatedPlayer(unsigned int r, const pingpong &pg) : rating(r), pingpong(pg)
{//将调用复制构造函数,由于没有申明它,所以就会调用 隐式的 pingpong ::pingpong(const pingpong &t)
//因为不存在 new,调用隐式没问提
}
注意
//调用的时候
创建派生类对象时,程序首先调用基类的构造函数,然后调用自己的;
析构时,先调用自己的析构函数,在调用基类的析构函数
//关系
派生和基类的关系
a:派生类可以使用基类的方法(非私有的)
b:基类的指针可以在不进行显示转换时指向派生对象;基类的引用亦可;反之不行
代码演示
RatedPlayer s(1120, player2);//派生类
pingpong &ss = s;//基类
2、继承:is-a 关系
- 共有继承
即派生类也是一个基类的对象,可以对基类对象执行任何操作。
继承只能添加东西,不能删除东西 - 私有继承
- 保护继承
3、多态继承和虚函数(方法)
虚函数;在基类中将会在派生类 中重新定义的方法;在基类中 声明虚函数,会被派生类自动声明成 虚方法。(建议还是加上virtual)
简而言之,当你需要重新实现某个函数时,需要在基类中把它声明为 virtual
多态;同一个指针,可以指向不同的对象;
test 是一个基类
RatedPlayer s(1120, player2);
cout << player2.getlastName() << "\n";
//多态的实现
pingpong *tt; //pingpong 是一个基类,RatePlayer是一个派生类
tt = new pingpong("james", "hua", false);
tt = new RatedPlayer(1120,"james", "hua", false);
//多态的实现
//虚析构函数 ,就是可以根据类型,去执行对应类的析构。
如果对应的成员有指针类型就需要释放。
4、静态联编和动态联编
静态联编;在编译过程中完成的;函数重载提升了编译时间
动态联编;在运行时,执行正确的代码这个过程。虚函数也提升了难度
- 虚函数的工作原理
在编译类的时候,基类对象包含一个指针,指向基类中所有虚函数的地址表(所有虚函数都存在这张表中)
- 在派生的时候;如果对虚函数有了新定义,将更新 虚函数表的地址;反之;则不更新
缺点与优点
缺:调用函数大的时候,都会去查表;每个对象都会增大,用来存储地址的空间;
优点:能够动态联编;即不同类型的类调用不同的虚函数。
哪些应该成为虚函数
1、在派生类中需要重新定义的
2、基类中有指针,需要被继承;则需要析构函数为虚函数。
5、访问控制
- protected
- private
区别:在派生类的时候,可以直接访问protected的成员;不能访问 private的成员;
对于不派生的情况,外界都不能访问。
6、继承与动态内存分配
知识点
只要类中有指针,就需要构造函数、析构函数、赋值运算符 中包含对指针的声明空间、以及释放
如果派生类中有指针,就需要显示调用析构、复制构造、赋值运算符
完整程序解读:
整合了继承、多态的使用;
链接:https://pan.baidu.com/s/11w-TCuQF84mtT8ThDvbg3g
提取码:zvbq