爱上c++的第六天(核心课程):继承和多态

简介: 学习c++的人一定知道的就是c++是面向对象设计的,而面对对象的三大特性就是封装,继承和多态,我们在刚开始学习的时候就已经学过封装了,今天我们主要来学习一下多态和继承。

你的c++学习路上明灯


继承:


一,基本介绍:


1,好处:


减少重复代码。


2,语法:


class 子类(派生类) : 继承方式 父类(基类)


3,


派生类中的成员包含两大部分:


一部分是从基类中继承来的东西,另一部分是自己增加的部分


从基类继承过来的表现其共性,而新增成员体现其个性


二,详细的使用


1.继承方式:


1)公共继承:父类权限不变;


2)保护继承:全部变成保护权限


3)私有继承:全部变为私有权限


上述三种继承方式都无法访问到分类的私有权限下的内容,但是可以访问到保护权限下的。


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//普通实现页面
//需要大量的重复代码,冗余。
//class Java {
//public:
//
//  void header() {
//    cout << "首页,公开课。。。。(公共头部)" << endl;
//  }
//  void footer() {
//    cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//  }
//  void content() {
//    cout << "Java学科视频" << endl;
//  }
//
//};
//
//class Python {
//public:
//
//  void header() {
//    cout << "首页,公开课。。。。(公共头部)" << endl;
//  }
//  void footer() {
//    cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//  }
//  void content() {
//    cout << "Python学科视频" << endl;
//  }
//
//};
//class Cpp {
//public:
//
//  void header() {
//    cout << "首页,公开课。。。。(公共头部)" << endl;
//  }
//  void footer() {
//    cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//  }
//  void content() {
//    cout << "Cpp学科视频" << endl;
//  }
//
//};
//继承页面
// 重复的部分只用写一次。
//公共页面类
class basepage {
public:
  void header() {
    cout << "首页,公开课。。。。(公共头部)" << endl;
  }
  void footer() {
    cout << "帮助中心,交流合作。。。(公共底部)" << endl;
  }
};
class Java : public basepage {
public:
  void content() {
    cout << "Java学科视频" << endl;
  }
};
class Python : public basepage {
public:
  void content() {
    cout << "Python学科视频" << endl;
  }
};
class Cpp :public basepage {
public:
  void content() {
    cout << "Cpp学科视频" << endl;
  }
};
void test1() {
  cout << "Java下载视频如下:" << endl;
  Java ja;
  ja.content();
  ja.footer();
  ja.header();
  cout << "------------------" << endl;
  cout << "Python下载视频如下:" << endl;
  Python py;
  py.content();
  py.footer();
  py.header();
  cout << "------------------" << endl;
  cout << "Cpp下载视频如下:" << endl;
  Cpp cp;
  cp.content();
  cp.footer();
  cp.header();
  cout << "------------------" << endl;
}
int main() {
  test1();
  return 0;
}


2.注意我上面的那句话,无法访问到,这就说明了,父类中私有成员也会被子类继承,只是由编译器隐藏后访问不到了。


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class base {
public:
  int m_A;
private:
  int m_B;
protected:
  int m_C;
};
class son1 :public base {
public:
  int m_D;
};
//父类所有的非静态成员属性都会被子类继承下去
//父类中的私有成员属性是被编译器给隐藏了。因此访问不到,但是确实是被继承下来了
void test1() {
  cout << "son1 的大小为:" << sizeof(son1) << endl;
  //所以此处的大小是16;
}
int main(){
  test1();
  return 0;
}


3.继承中构造函数和析构函数的调用顺序


创建子类对象后,先构造父类,再构造子类,析构顺序相反。


4.同名成员处理


1)子类对象可以直接访问到子类中的同名成员,


2)子类对象加作用域可以访问到父类同名成员


3)当子类与父类拥有同名的成员函数,子类会隐藏父类中的同名函数,一定要加作用域才能够访问


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class base {
public:
  int m_A=200;
  void func() {
    cout << "base-func()函数的调用" << endl;
  }
  void func(int a) {
    cout << "base-func(int a)函数的调用" << endl;
  }
};
class son1 :public base {
public:
  int m_A=100;
  void func() {
    cout << "son-func()函数的调用" << endl;
  }
};
//同名成员函数处理
void test1() {
  son1 s;
  //子类对象可以直接访问到子类中的同名成员
  s.func();
  //子类对象加作用域可以访问到父类的同名成员
  s.base::func();
  //当子类对象与父类对象拥有同名的成员函数,子类会隐藏父类中的同名函数,一定要加作用域才能够访问到哦
}
//同名成员属性处理
void test2() {
  son1 s;
  cout << s.m_A << endl;
  cout << s.base::m_A << endl;
}
int main() {
  test1();
  test2();
  return 0;
}


5.同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式(通过对象,通过类名)


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Base {
public:
  static int m_A;
  static void func() {
    cout << "Base-func()的调用" << endl;
  }
  static void func(int a) {
    cout << "Base-func(int a)的调用" << endl;
  }
};
int Base::m_A = 100;
class Son:public Base {
public:
  static int m_A;
  static void func() {
    cout << "Son-func()的调用" << endl;
  }
};
int Son::m_A = 200;
void test1() {
  //1.通过对象访问
  Son s;
  cout << "通过对象访问" << endl;
  cout << s.m_A << endl;
  cout << s.Base::m_A << endl;
  //2,通过类名访问
  cout << "通过类名访问" << endl;
  cout << Son::m_A << endl;
  //第一个 ::代表通过类名访问,第二个 ::代表作用域
  cout << Son::Base::m_A << endl;
}
int main() {
  test1();
  return 0;
}
//同名静态成员处理方式和非静态成员处理方式一样,只不过是有了两种访问方式
//通过类名和通过对象


6.多继承语法


1)c++允许一个类继承多个类(一个子类继承多个父类)


2)多继承可能会引发父类中有同名成员出现,需要加一个作用域区分


3)c++实际开发不建议用多继承(容易引发一些不必要的冲突)


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Base1 {
public:
  int m_A;
  Base1() {
    m_A = 100;
  }
};
class Base2 {
public:
  int m_A;
  Base2() {
    m_A = 200;
  }
};
class Son :public Base1, public Base2 {
public:
  int m_C;
  Son() {
    m_C = 300;
  }
};
void test1() {
  Son s;
  //大小依然是所有的数据大小之和
  cout << sizeof(s) << endl;
  //多继承中如果父类中出现了同名情况,子类使用的时候要加作用域
  cout << s.Base1::m_A << endl;
  cout << s.Base2::m_A << endl;
}
int main() {
  test1();
  return 0;
}


7.菱形继承


菱形继承带来的问题:


子类继承两份相同的数据,导致资源浪费和二义性。


可以用虚继承的方法来解决


所谓虚继承每一个派生类会给自己的子类继承虚基类指针(vbptr)和一份虚基类表,表中记录了虚基类与本类与本类的偏移量。


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Animal {
public:
  int m_Age;
};
//虚继承就是再继承方式的前面加上virtual关键字
//虚继承就是菱形继承问题的解决方法
class sheep:virtual public Animal{};
class tuo :virtual public Animal{};
class sheeptuo : public sheep, public tuo{};
void test1() {
  sheeptuo st;
  //两个派生类都从基类中继承了相同的数据,浪费空间,只需要留一份就好了
  //应用虚继承以后,两个派生类的子类只从其中继承了一份数据,且数据值以最后一次修改为准
  st.sheep::m_Age = 100;
  st.tuo::m_Age = 200;
  cout << st.sheep::m_Age << endl;
  cout << st.tuo::m_Age << endl;
}
int main() {
  test1();
  return 0;
}


多态:


一,基本介绍


多态分为两类:


1)静态多态:函数重载和运算符重载,复用函数名


2)动态多态:派生类和虚函数实现运行时多态。


区别:静态多态的函数地址早绑定---编译阶段确定函数地址


          动态多态的函数地址晚绑定---运行阶段确定函数地址


***c++中允许父子类之间的类型转换


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Animal {
public:
  //函数前面加上virtual关键字,变成虚函数,那么编译器在编译时就不能确定函数调用了
  //即:函数地址的先后绑定
  virtual void Speak() {
    cout << "动物在说话" << endl;
  }
};
class Cat :public Animal {
public:
  //重写:函数返回值类型  函数名 参数列表 完全一致
  //所以这里的virtual可写可不写
  virtual void Speak() {
    cout << "小猫在说话" << endl;
  }
};
class Dog :public Animal {
public:
  void Speak() {
    cout << "小狗在说话" << endl;
  }
};
//回调函数,提供多个选择
//c++中允许父子类之间的类型转化
void dospeak(Animal &animal) {
  animal.Speak();
}
void test1() {
  Cat cat;
  dospeak(cat);
  Dog dog;
  dospeak(dog);
}
int main() {
  test1();
  return 0;
}


二,详细的使用


1.动态多态满足条件:


1)有继承关系


2)子类重写父类的虚函数


2.使用动态多态:


父类的指针或引用指向子类对象


3.重写:函数返回值类型    函数名,参数列表完全一致称为重写。


4.多态的优点


1)代码组织结构清晰


2)可读性强


3)利于前期和后期的扩展以及维护


5.纯虚函数


virtual  返回值类型  函数名  (参数列表)=0;


6.抽象类特点:


1)无法实例化对象


2)子类必须重写抽象类中的纯虚函数,否则也属于抽象类。


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Base {
public:
  //纯虚函数
  //父类的虚函数是用不上的,写了也是白写,所以就出现了纯虚函数的概念
  virtual void func() = 0;
  //一旦有了纯虚函数,该类就称作抽象类
  //不能实例化对象
  //且子类也必须重写虚函数,不然子类也成了抽象类,也无法实例化对象
};
class Son : public Base {
public:
  void func() {
    cout << "func()函数的调用" << endl;
  }
};
void test1() {
  //多态的意义就是
  //定义一个父类的指针,指向它的任意一个我们想要使用的子类对象,
  //从而能使我们调用到该子类对象的函数实现
  Base* b = new Son;
  b->func();
  delete b;
}
int main() {
  test1();
  return 0;
}


7.虚析构和纯虚析构的共性


1)可以解决父类指针释放子类对象时不干净


2)都需要有具体的函数实现


区别:如果是纯虚析构,该类属于抽象类,无法实例化对象。


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
class Animal {
public:
  virtual void Speak() = 0;
  Animal() {
    cout << "Animal构造函数的调用" << endl;
  }
  //利用纯析构可以解决父类指针释放子类对象时不干净的问题
  /*virtual ~Animal() {
    cout << "Animal析构函数的调用" << endl;
  }*/
  //如果不想让该类实例化对象,就写成纯虚析构
  virtual ~Animal() = 0;
};
Animal::~Animal() {
  cout << "Animal纯虚析构函数的调用" << endl;
}
class Cat :public Animal {
public:
  virtual void Speak() {
    cout << "小猫在说话" << endl;
  }
  Cat(string name) {
    cout << "Cat构造函数的调用" << endl;
    m_Name = new string(name);
  }
  ~Cat() {
    cout << "Cat析构函数的调用" << endl;
    if (m_Name != NULL) {
      delete m_Name;
      m_Name = NULL;
    }
  }
  string* m_Name;
};
void test1() {
  Animal* a = new Cat("Tom");
  a->Speak();
  //父类指针在析构的时候,并不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄漏
  delete a;
}
int main() {
  test1();
  return 0;
}


8.实例


给大家两个实例练练手


1)


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//制作饮品-多态
class MakeDrink {
public:
  //煮水
  virtual void Boil() = 0;
  //冲泡
  virtual void Brew() = 0;
  //倒入杯中
  virtual void Pour() = 0;
  //加入辅料 
  virtual void Add() = 0;
  void makedrink() {
    Boil();
    Brew();
    Pour();
    Add();
  }
};
//制作咖啡
class Coffee : public MakeDrink {
public:
  //煮水
  virtual void Boil() {
    cout << "煮咖啡水" << endl;
  }
  //冲泡
  virtual void Brew() {
    cout << "冲泡咖啡" << endl;
  }
  //倒入杯中
  virtual void Pour() {
    cout << "倒入咖啡杯中" << endl;
  }
  //加入辅料 
  virtual void Add() {
    cout << "加入牛奶和糖" << endl;
  }
};
//制作茶
class Tea : public MakeDrink {
public:
  //煮水
  virtual void Boil() {
    cout << "煮茶水" << endl;
  }
  //冲泡
  virtual void Brew() {
    cout << "冲泡茶" << endl;
  }
  //倒入杯中
  virtual void Pour() {
    cout << "倒入茶杯中" << endl;
  }
  //加入辅料 
  virtual void Add() {
    cout << "加入茶叶" << endl;
  }
};
void dodrink(MakeDrink * md) {//MakeDrink * md = 子类对象
  md->makedrink();
  delete md;
}
void test1() {
  //MakeDrink* md = new Coffee;
  //MakeDrink& md = new Coffee;
  dodrink(new Coffee);
  cout << "-----------------" << endl;
  dodrink(new Tea);
}
int main() {
  test1();
  return 0;
}


2)制作电脑


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//抽象不同的零件类
//抽象CPU类
class CPU {
public:
  virtual void calculate() = 0;
};
//抽象显卡类
class xianka{
public:
  virtual void display() = 0;
};
//抽象内存条类
class Memory {
public:
  virtual void storage() = 0;
};
//电脑类
class Computer {
public:
  Computer(CPU* cpu,xianka* xk,Memory* mem) {
    m_mem = mem;
    m_xk = xk;
    m_cpu = cpu;
  }
  //提供工作的函数
  void work() {
    m_cpu->calculate();
    m_xk->display();
    m_mem->storage();
  }
  ~Computer() {
    if (m_cpu != NULL) {
      delete m_cpu;
      m_cpu = NULL;
    }
    if (m_xk != NULL) {
      delete m_xk;
      m_xk = NULL;
    }
    if (m_mem != NULL) {
      delete m_mem;
      m_mem = NULL;
    }
  }
private:
  CPU* m_cpu;
  xianka* m_xk;
  Memory* m_mem;
};
//具体厂商
//inter厂商
class IntelCPU :public CPU {
public:
  virtual void calculate() {
    cout << "Inter的CPU" << endl;
  }
};
class Intelxianka :public xianka {
public:
  virtual void display() {
    cout << "Inter的xianka" << endl;
  }
};
class IntelMemory :public Memory {
public:
  virtual void storage() {
    cout << "Inter的Memory" << endl;
  }
};
//联想厂商
class Lenovo :public CPU {
public:
  void calculate() {
    cout << "Lenovo的CPU" << endl;
  }
};
class Lenovoxianka :public xianka {
public:
  void display() {
    cout << "Lenovo的xianka" << endl;
  }
};
class LenovoMemory :public Memory {
public:
  void storage() {
    cout << "Lenovo的Memory" << endl;
  }
};
void test1() {
  //创建第一台电脑
  CPU* intelCpu = new IntelCPU;
  xianka* intelxianka = new Intelxianka;
  Memory* intelmem = new IntelMemory;
  Computer computer1(intelCpu, intelxianka, intelmem);
  computer1.work();
  cout << "------------------" << endl;
  //创建第二台电脑
  Computer computer2(new IntelCPU, new Intelxianka, new IntelMemory);
  computer2.work();
}
int main() {
  test1();
  return 0;
}


东西很多,希望大家认真学习。

目录
相关文章
|
1月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
33 1
【C++】继承
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++】多态
多态是面向对象编程中的重要特性,允许通过基类引用调用派生类的具体方法,实现代码的灵活性和扩展性。其核心机制包括虚函数、动态绑定及继承。通过声明虚函数并让派生类重写这些函数,可以在运行时决定具体调用哪个版本的方法。此外,多态还涉及虚函数表(vtable)的使用,其中存储了虚函数的指针,确保调用正确的实现。为了防止资源泄露,基类的析构函数应声明为虚函数。多态的底层实现涉及对象内部的虚函数表指针,指向特定于类的虚函数表,支持动态方法解析。
32 1
|
2月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
47 2
C++入门12——详解多态1
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
92 11
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
67 1
|
2月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
89 1
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
49 1
|
2月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
22 0
|
2月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
41 0
|
2月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
44 0