目录

1.继承的权限有三种

(1)public继承

(2)protected继承

(3)private继承

总结

2.创建和销毁的执行过程

(1)类中有成员对象情况

(2)继承时情况

(3)创建和销毁综合

3.多继承

多继承的问题

4.菱形继承(钻石继承)

(1)解决方式1:通过作用域限定符的方式进行区分

(2)解决方式2:通过虚继承的方式,从公共基类中得到一份成员,避免了二义性

5.多态

(1)子类可以转成基类的情况

(2)多态的实现

(3)多态练习

(4)多态缺陷


1.继承的权限有三种

不管哪种继承方式,基类私有成员在子类中都不能直接访问

公共继承全不变,保护继承变保护,私有继承变私有

(1)public继承

基类的public成员 基类的protected成员 基类的private成员
在子类中访问的访问权限 成为子类public成员 成为子类public成员 不能直接访问
#include <iostream>using namespace std;
class Father{
public:string publicStr;
protected:string protectedStr;
private:string privateStr;
};
class Son:public Father{
public:void method(){cout<<publicStr<<endl;cout<<protectedStr<<endl;//cout<<privateStr<<endl;}
};int main(){Son s;//公有继承时//父类中公共属性,在子类中也是public权限,全局可以访问s.publicStr;
}

(2)protected继承

基类的public成员 基类的protected成员 基类的private成员
在子类中访问的访问权限 成为子类protected成员 成为子类protected成员 不能直接访问
#include <iostream>
using namespace std;
class Father{
public:
string publicStr;
protected:
string protectedStr;
private:
string privateStr;
};
class Son:protected Father{
public:
void method(){cout<<publicStr<<endl;cout<<protectedStr<<endl;
}
};
class GrandSon:public Son{
void method(){cout<<publicStr<<endl;cout<<protectedStr<<endl;
}};
int main()
{Son s;// 子类保护继承时,父类的公共成员,在子类中变成保护成员,类外就访问不到//s.publicStr;}

(3)private继承

基类的public成员 基类的protected成员 基类的private成员
在子类中访问的访问权限 成为子类private成员 成为子类private成员 不能直接访问
#include <iostream>
using namespace std;
class Father{
public:string publicStr;
protected:string protectedStr;
private:string privateStr;
};class Son:private Father{
public:
void method(){cout<<publicStr<<endl;cout<<protectedStr<<endl;
}
};
class GrandSon:public Son{
void method(){
//        cout<<publicStr<<endl;   //这些成员变量在Son类中是私有的,访问不到
//        cout<<protectedStr<<endl;
}};
int main()
{Son s;}

总结

2.创建和销毁的执行过程

(1)类中有成员对象情况

类的数据成员也可以时个对象,成为成员对象

当类中有成员对象时,实例化对象先调用成员对象的构造函数,再调用对象自己的构造函数

//小孔成(成员对象)像    翻着来

#include <iostream>using namespace std;//has a
class Engine{
public:Engine(){cout<<"Engine的构造函数"<<endl;}~Engine(){cout<<"Engine的析构函数"<<endl;          //对象销毁前的处理}
};class Car{
public:Engine e;   //成员对象Car(){cout<<"Car的构造函数"<<endl;}~Car(){cout<<"Car的析构函数"<<endl;          //对象销毁前的处理}
};int main(){Car c;
}

运行结果:

*****先调用成员对象的构造函数,再调用自己的,析构函数时相反*****

(2)继承时情况

继承时构造和析构的顺序

先调用父类的构造函数,完成从父类继承部分的初始化。然后调用子类自己的构造函数,完成子类中特有的成员初始化。

#include <iostream>using namespace std;class Father{
public:Father(){cout<<"Father的构造函数"<<endl;}~Father(){cout<<"Father的析构函数"<<endl;          //对象销毁前的处理}
};class Son:public Father{
public:Son(){cout<<"Son的构造函数"<<endl;}~Son(){cout<<"Son的析构函数"<<endl;          //对象销毁前的处理}
};int main(){Son s;
}

运行结果:

(3)创建和销毁综合

创建的顺序和销毁的顺序  是对称的

#include <iostream>using namespace std;
class Value{
private:string str;
public:Value(string str){this->str=str;cout<<str<<"创建了"<<endl;}~Value(){cout<<str<<"销毁了"<<endl;}
};class Father{
public:static Value s_v;   //静态成员类内声明 类外初始化Value v=Value("父类的成员对象");Father(){cout<<"Father的构造函数"<<endl;}~Father(){cout<<"Father的析构函数"<<endl;}
};
Value Father::s_v=Value("父类的静态成员对象");class Son:public Father{
public:static Value s_v;//静态成员类内声明,类外初始化Value v=Value("子类的成员对象");Son(){cout<<"Son的构造函数"<<endl;}~Son(){cout<<"Son的析构函数"<<endl;}
};
Value Son::s_v=Value("子类的静态成员对象");int main(){cout<<"--程序开始之前--"<<endl;{ //局部作用域 栈内存对象出了这个范围(两个花括号之间)就会销毁Son s;}cout<<"---程序结束---"<<endl;}

运行结果如下:

3.多继承

一个类的基类有多个就是多继承。可以继承多个基类中的成员

#include <iostream>
using namespace std;
class Bed{
public:void lay(){cout<<"可以躺着"<<endl;}
};
class Sofa{
public:void sit(){cout<<"可以坐着"<<endl;}
};
class SofaBed:public Sofa,public Bed{};int main(){SofaBed sf;sf.lay();sf.sit();
}

多继承的问题

多继承实际开发的极少,多继承所增加程序的复杂性和困难性,比他的便利性要高

当多个基类中有重名的成员时,直接访问会产生二义性,可以使用作用域限定符::的方式进行区分

#include <iostream>
using namespace std;
class Bed{
public:void lay(){cout<<"可以躺着"<<endl;}void position(){cout<<"放在卧室"<<endl;}int price=4000;
};
class Sofa{
public:void sit(){cout<<"可以坐着"<<endl;}void position(){cout<<"放在课厅"<<endl;}int price=2000;
};
class SofaBed:public Sofa,public Bed{};int main(){SofaBed sf;sf.lay();sf.sit();//沙发床中继承了 Bed::position 和 Sofa::position//sf.position(); //会产生歧义 犯了二义性sf.Bed::position();sf.Sofa::position();//cout<<sf.price<<endl;//二义性cout<<sf.Sofa::price<<endl;cout<<sf.Bed::price<<endl;}

4.菱形继承(钻石继承)

如果一个基类有两个派生类,这两个派生类有作为父类,派生出子类,这时子类如果直接访问基类成员,也会产生二义性问题

(1)解决方式1:通过作用域限定符的方式进行区分

#include <iostream>
using namespace std;
class Furniture{
public:void show(){cout<<"一个家具" <<endl;}
};class Bed:public Furniture{
public:};
class Sofa:public Furniture{
public:};
class SofaBed:public Sofa,public Bed{//从Sofa这条路径得到一个show()//从Bed这条路径也得到一个show()
};int main(){SofaBed sf;//sf.show();  //ambiguoussf.Bed::show();sf.Sofa::show();}

(2)解决方式2:通过虚继承的方式,从公共基类中得到一份成员,避免了二义性

#include <iostream>
using namespace std;
class Furniture{  //虚基类
public:void show(){cout<<"一个家具" <<endl;}int a=10;
};class Bed:virtual public Furniture{
public:};
class Sofa:virtual public Furniture{
public:};
class SofaBed:public Sofa,public Bed{//从Sofa这条路径得到一个show()//从Bed这条路径也得到一个show()
};int main(){SofaBed sf;//sf.show();  //ambiguouscout<<sf.a<<endl;cout<<&(sf.a)<<endl;cout<<&(sf.Bed::a)<<endl;cout<<&(sf.Sofa::a)<<endl;}

5.多态

多态按照字面的意思可以认为是“多种状态”,可以简单概括为“一个接口,多种状态”,即程序在运行时动态决定调用的代码。

多态与模板的区别在于,模板针对不同的数据类型采用样的策略,而多态针对不同的数据类型采用不同的策略。

多态条件:

1.公有继承

2.基类的指针或者引用指向派生类的对象

3.子类覆盖基类中的虚函数

虚函数就是用virtual关键字修饰的函数

(1)子类可以转成基类的情况

在公有继承情况下:

基类对象可以用派生类对象赋值

基类的引用可以指向派生类对象

基类的指针可以指向派生类对象地址

上述过程称为向上转换,但是只能够访问基类中公共的部分

#include <iostream>using namespace std;
class Person{
private:string name;string sex;
public:Person(string name,string sex){this->name=name;this->sex=sex;}void show(){cout<<"姓名:"<<name<<"性别:"<<sex<<endl;}
};
class Employee:public Person{
private:double salary;  //子类新增int work_id;
public:Employee(string name,string sex,double salary,int work_id):Person(name,sex){this->salary=salary;this->work_id=work_id;}void show(){Person::show();cout<<"工资:"<<salary<<"工号:"<<work_id<<endl;}};int main(){Employee e("张三","男",2000,2023001);e.show();Person p=e;p.show();  //姓名:张三 性别 男//e.Person::show();Person& p2=e;p2.show();Person* p3=&e;p3->show();
}

未实现运行多态的问题

没有实现运行时多态出现的问题:只是根据接口参数的类型,调用其方法,并不能根据传入的实际对象类型,调用实际对象的方法。

#include <iostream>
using namespace std;
class Animal{
public:void eat(){cout<<"吃东西"<<endl;}
};class Cat:public Animal{
public:void eat(){cout<<"吃猫粮"<<endl;}
};class Dog:public Animal{
public:void eat(){cout<<"吃狗粮"<<endl;}
};class Panda:public Animal{
public:void eat(){cout<<"吃竹子"<<endl;}
};//三个类需要三个独立的接口,如果100个需要100个。。很麻烦
//void method(Cat& c){
//    c.eat();
//}
//void method(Dog& d){
//    d.eat();
//}
//void method(Panda * p){
//    p->eat();
//}//写出公共接口,这样接收Animal的所有子类对象
void method(Animal& a){a.eat();
}void method(Animal *a){a->eat();
}int main(){Cat c;method(c);     // 吃东西Dog dog2;method(dog2);    // 吃东西Panda panda2;method(&panda2);   // 吃东西}

地址的早绑定:编译的时候已经确定调用哪个类的函数

地址的晚绑定:需要运行的时候再决定调用哪个类的函数,需要用到虚函数,这样才能实现运行时多态

(2)多态的实现

在派生类中,使用之前的函数隐藏的方式重新实现一个基类中的虚函数,此时就函数覆盖,函数覆盖与虚函数具有以下特点:

1.当函数覆盖成功时,虚函数具有传递性

2.C++11中可以在派生类的新覆盖的函数后增加override关键字进行覆盖是否成功的验证

3.成员函数与析构函数可以定义为虚函数,静态成员函数与构造函数不可以定义为虚函数,因为构造函数不能被派生类继承,也就没办法覆盖重写。静态成员函数与对象无关

4.如果成员函数的声明与定义分离,virtual关键字只需加在声明处

记忆:

传递override来验证

成员析构虚函数

分离只需加声明

重载:同一作用域(类),同名函数,参数的顺序、个数、类型不同都可以重载。函数的返回值类型不能作为重载条件(函数重载、运算符重载)

重定义:有继承,子类重定义父类的同名函数(非虚函数),参数顺序、个数、类型可以不同。

子类的同名函数会屏蔽父类的所有同名函数(如果想访问父类隐藏的函数可以通过作用域解决)

重写(覆盖):有继承,子类重写父类的虚函数,返回值类型、函数名、参数顺序、个数、类型都必须一致。

#include <iostream>
using namespace std;
class Animal{
public://声明和定义分离的 virtual关键字只需加在声明处void virtual eat();};
void Animal::eat(){cout<<"吃东西"<<endl;
}
class Cat:public Animal{
public://override用来检测,有没有重写父类中的虚函数void eat() override{cout<<"吃猫粮"<<endl;}
};class Dog:public Animal{
public:void eat(){cout<<"吃狗粮"<<endl;}
};class Panda:public Animal{
public:void eat(){cout<<"吃竹子"<<endl;}
};//三个类需要三个独立的接口,如果100个需要100个。。很麻烦
//void method(Cat& c){
//    c.eat();
//}
//void method(Dog& d){
//    d.eat();
//}
//void method(Panda * p){
//    p->eat();
//}//写出公共接口,这样接受Animal的所有子类对象
void method(Animal& a){a.eat();
}void method(Animal *a){a->eat();
}int main(){Cat c;method(c);     // 吃猫粮Dog dog2;method(dog2);    // 吃狗粮Panda panda2;method(&panda2);   // 吃竹子}

(3)多态练习

Role 角色类

Role派生出 AD战士类 AP 法师类 ADC射手类 ,里面都有一个attack()方法

有个公共的接口mehtod,可以接受所有角色的派生类对象,根据传入对象的不同,调用不同的方式

比如传入AD对象 会显示出AD对象的攻击方式

传入AP对象 会显示出AP对象的攻击方式

#include <iostream>using namespace std;
class Role{
public:virtual void attack();
};
void Role::attack(){cout<<"攻击方法"<<endl;
}class AD:public Role{void attack(){cout<<"AD: "<<"物理伤害"<<endl;}
};class AP:public Role{void attack(){cout<<"AP: "<<"法术伤害"<<endl;}
};class ADC:public Role{void attack(){cout<<"ADC: "<<"真实伤害"<<endl;}
};void method(Role& r){r.attack();
}
void method(Role* r){r->attack();
}
int main(){AD d;method(d);AP p;method(&p);ADC *c=new ADC;method(c);
}

(4)多态缺陷

可能造成内存泄漏

#include <iostream>
using namespace std;class Animal{
public://声明和定义分离的virtual关键字只需加在声明处virtual void eat(){cout<<"吃东西"<<endl;}Animal(){cout<<"Animal的构造函数"<<endl;}~Animal(){cout<<"Animal的析构函数"<<endl;}
};class Cat:public Animal{
public:Cat(){cout<<"Cat的构造函数"<<endl;}void eat() override{cout<<"吃猫粮"<<endl;}~Cat(){cout<<"Cat的析构函数"<<endl;}
};//写出公共接口,这样接受Animal的所有子类对象void method(Animal& a){a.eat();
}
void method(Animal *a){a->eat();
}
int main(){{Animal *a=new Cat;delete a;a=NULL;}
}

运行结果:

这时只会调用Animal的析构函数,Cat的析构函数没有调用,可能会造成Cat中的为成员分配的空间没有释放,造成内存泄漏。

解决方式:基类的析构函数前加virtual关键字

#include <iostream>
using namespace std;
class Animal{
public://声明和定义分离的virtual关键字只需加在声明处virtual void eat(){cout<<"吃东西"<<endl;}Animal(){cout<<"Animal的构造函数"<<endl;}virtual ~Animal(){cout<<"Animal的析构函数"<<endl;}
};class Cat:public Animal{
public:Cat(){cout<<"Cat的构造函数"<<endl;}~Cat(){cout<<"Cat的析构函数"<<endl;}
};//写出公共接口,这样接收Animal的所有子类对象
void method(Animal& a){a.eat();
}
void method(Animal *a){a->eat();
}int main(){{Animal *a=new Cat;delete a;a=NULL;}
}

运行结果如下:

6-C++中继承的权限有三种情况、创建和销毁的执行过程、多继承、菱形继承、多态的相关知识点相关推荐

  1. 面试中常被问到的(12)函数调用执行过程

    第一步:函数调用 1.将函数调用语句下一条语句的地址保存到在栈中,以便哈数调用完成后返回.(将函数放到栈空间中称为压栈). 2.对实参表从后向前,一次计算出实参的值,并且将值压栈. 3.跳转到函数体处 ...

  2. 必须采用初始化列表一共有三种情况

    有一个类B继承自类A,他们数据成员如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A { - private: int &a; }; class B ...

  3. Java中的访问控制权限

    简介 Java中为什么要设计访问权限控制机制呢?主要作用有两点: (1)为了使用户不要触碰那些他们不该触碰的部分,这些部分对于类内部的操作时必要的,但是它并不属于客户端程序员所需接口的一部分. (2) ...

  4. C++57个入门知识点_50 菱形继承与虚继承(C++中语法允许多重继承造成菱形继承;会造成近亲结婚的问题;可以通过虚继承的方式解决;实际项目中不多用多重继承)

    上篇C++57个入门知识点_49 多重继承与组合(一个类同时具有多个类的属性的方法:多重继承或者组合:多重继承:一个类同时继承多个类:多重继承构造和析构的顺序与普通继承类似:组合:类中包含多个成员对象 ...

  5. 菱形继承,多继承,虚继承、虚表的内存结构全面剖析(逆向分析基础)

    // 声明:以下代码均在Win32_Sp3   VC6.0_DEBUG版中调试通过.. 在逆向还原代码的时候,必须得掌握了菱形继承,多继承,虚继承虚函数的内存虚表结构.所以,这篇文章献给正在学习C++ ...

  6. Yii2中关于组件的注册以及创建的方法详解

    2019独角兽企业重金招聘Python工程师标准>>> 了解yii组件注册与创建的过程,并发现原来yii组件注册之后并不是马上就去创建的,而是待到实际需要使用某个组件的时候再去创建对 ...

  7. 【Groovy】Groovy 脚本调用 ( Groovy 类中调用 Groovy 脚本 | 创建 GroovyShell 对象并执行 Groovy 脚本 | 完整代码示例 )

    文章目录 一.Groovy 类中调用 Groovy 脚本 1.创建 GroovyShell 对象并执行 Groovy 脚本 2.代码示例 二.完整代码示例 1.调用者 Groovy 脚本的类 2.被调 ...

  8. 在保护继承中基类的共有成员_C++学习刷题13--继承的实现、继承的方式

    一.前言 本部分为C++语言刷题系列中的第13节,主要讲解这几个知识点:继承的实现.继承的方式.欢迎大家提出意见.指出错误或提供更好的题目! 二.知识点讲解 知识点1:继承的实现,可以理解派生类拥有成 ...

  9. linux 系统中的文件权限

    linux中的文件权限大家一般熟悉的就是777了,实际上文件的权限是有7777的,之前遇到没搞明白,还觉得是文件系统出问题了呢,现在想想当时自己还是太轻浮了,没仔细去看,今天闲着没事终于可以吧剩下的7 ...

最新文章

  1. 什么是B/S模式?什么是C/S模式?
  2. Zookeeper客户端
  3. 一句话征服了美国人,这位饱受争议的数学博士竟从未上过学?
  4. 《测试驱动数据库开发》——1.2 谁是目标读者
  5. 检测客户端是否安装 Silverlight 插件和判断IE是否安装或支持Silverlight(限IE)
  6. iOS网络请求之multipart/form-data提交数据
  7. paip.语义分析--分词--常见的单音节字词 2_deDuli 单字词 774个
  8. 计算机视频剪辑教程,电脑怎么剪辑视频?新手都能上手的视频剪辑教程分享
  9. 基于ssm的仿微博系统的设计与实现
  10. c语言中循环指令m=_crol_(m,1),单片机中关于_crol_函数 aa=_crol_(aa,1),执行八次之后流水灯为什么回到了初始状态继续循环 ?...
  11. hexo博客中如何插入图片
  12. 联通速品简易测试报告
  13. 无人机相关的基本术语小结
  14. Linux下异步IO(libaio)的使用以及性能
  15. 计算机科学着重于理论和算法,大学计算机-中国大学mooc-题库零氪
  16. 2017年加密货币进入国际金融体系
  17. ASP.NET 海南热带瓜菜百科网信息管理系统的设计与实现-王翔-专题视频课程
  18. WaveTone 2.67原创汉化版扒谱辅助教程
  19. 微软官方补丁6B BUG造成WIN7系统蓝屏解决方案
  20. 找不到wifi,提示适配器的驱动程序可能出现问题

热门文章

  1. 本特利电缆330130的安装
  2. 白盒测试中SC、DC、CC、CDC、MCC、MCDC含义区别
  3. 美的集团计算机综合面试群面,美的集团面试经验
  4. 2020年汽车驾驶员(中级)考试APP及汽车驾驶员(中级)考试软件
  5. matlab 硬阈值,matlab小波除噪,为何硬阈值和软阈值除躁信噪比一样了?
  6. java metrics_Java Metrics工具介绍
  7. 一个excel(20M)就能干趴你的poi,你信吗(附源码)?
  8. c 调用java post方法_.NET(C#) Fluent HTTP (Flurl Get和Post请求)使用方法及示例代码
  9. 理解IO流的使用C/C++
  10. ios助手开发系列(三):打开设备连接,获取设备基本信息