C++面向对象核心-权限-多态

news2024/11/12 21:12:58

1、权限

1.1 权限修饰符

三种权限,一共对应九种场景。要做到心中有表,遇到任何一种场景都能直接反映出是否能访问。

类内

派生类中

全局

private

×

×

protected

×

public

#include <iostream>
using namespace std;
class Base
{
protected:
//类内 派生类内访问,
    string s = "保护权限";
public:
    Base()
    {
        cout << s << endl;
    }
};
class Son:public Base
{
public:
    Son()
    {
        cout << s << endl;
    }
};
int main()
{
//    Base b1;
    Son s1;
//    cout << s1.s << endl; // 错误 s是保护权限
    return 0;
}

1.2 不同权限的继承

1.2.1 公有继承 public

上面的代码中一直使用的就是公有继承,公有继承也是使用的最多的一种继承方式。

共有继承当中,派生类可以继承基类的成员,但是不可访问基类的私有成员,基类的公有成员与保护成员在派生类中权限不变。

#include <iostream>

using namespace std;

class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};

class Son:public Base
{
public:
    Son()
    {
//        cout << str1 << endl; // 错误 str1为私有成员
        cout << str2 << endl;
        cout << str3 << endl;
    }
};


int main()
{
    Son s1;
    return 0;
}

1.2.2 保护继承 protected

保护继承中,派生类可以继承基类的成员,不可访问基类的私有成员,基类的公有成员与保护成员在派生类中的权限都是保护权限。(只能在基类与派生类中访问,外部无法访问)。

#include <iostream>

using namespace std;

class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};

// 保护继承
class Son:protected Base
{
public:
    Son()
    {
//        cout << str1 << endl; // 错误 str1为私有成员
        cout << str2 << endl;
        cout << str3 << endl;
    }
};


int main()
{
    Son s1;
//    s1.str3; //错误,str3在保护继承的派生类中为保护成员,外部无法访问
    return 0;
}

1.2.3 私有继承 private

私有继承中,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中的权限都是私有权限。

#include <iostream>

using namespace std;

class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};

// 私有继承
class Son:private Base
{
public:
    Son()
    {
//        cout << str1 << endl; // 错误 str1为私有成员
        cout << str2 << endl;
        cout << str3 << endl;
    }
};


int main()
{
    Son s1;
//    s1.str3; //错误,str在私有继承的派生类中为私有成员
    return 0;
}

2、多态

2.1 函数覆盖 override 

函数覆盖与函数隐藏比较相似,但是函数隐藏不支持多态,而函数覆盖是多态的必备条件。

函数覆盖是指在父类和子类之间存在继承关系时,子类定义了与父类中同名的函数,并且函数的参数类型、返回值类型必须与父类中的相应函数一致。当子类对象调用该同名函数时,会自动调用子类中的函数,而不是父类中的函数。这种机制就是函数覆盖。

(函数隐藏是指在派生类中定义了与基类中同名的函数,从而隐藏了基类中的函数。当通过派生类的对象调用该函数时,实际上调用的是派生类中定义的函数,而不是基类中的函数。函数隐藏发生在不同作用域,即派生类的作用域中隐藏了基类的函数。)

在编程方式上,函数覆盖与函数隐藏有以下几点区别:

  • 被覆盖的基类函数必须是虚函数。
  • 在C++中,可以在派生类中新覆盖的函数上使用override 关键字验证覆盖是否成功

一个函数使用virtual关键字修饰,就是虚函数。虚函数是函数覆盖的前提。在Qt Creator中函数名称使用斜体字。

虚函数具有以下性质:

  • 虚函数具有传递性,基类中被覆盖的函数是虚函数,派生类中新覆盖的函数也是虚函数。
  • 只有普通成员函数与析构函数可以声明为虚函数。
  • 如果虚函数的声明与定义分离,virtual关键字只需要修饰到声明处。不能写到定义处。

#include <iostream>
using namespace std;
class Animal
{
 public:
    // 虚函数
    virtual void eat();
    void test()
    {
    }
};
void Animal::eat()
{
    cout << "动物爱吃饭" << endl;
}
class Dog:public Animal
{
    void eat() override
    {
        cout << "狗爱吃骨头" << endl;
    }
//    void test() override
//    {
//    }
};
int main()
{
    Dog d1;
    return 0;
}

2.2 多态的概念

多态可以理解为“一种接口,多种状态”,只需要编写一个函数接口,根据传入的参数类型,执行不同的策略代码。

多态的使用需要具备三个前提条件:

  • 公有继承
  • 函数覆盖(在派生类内声明一个与基类公共成员函数函数名及参数一致的函数)
  • 基类的引用/指针指向派生类对象

#include <iostream>

using namespace std;

class Animal
{
 public:
    // 虚函数
    virtual void eat()
    {
        cout << "动物爱吃饭" << endl;
    }
};
class Dog:public Animal
{
public:
    void eat() override
    {
        cout<<"狗爱吃骨头"<<endl;
    }
};
class Cat:public Animal
{
public:
    void eat()
    {
        cout<<"猫爱吃鱼"<<endl;
    }
};
void animal_eat(Animal *al)
{
    al->eat();
}
void animal_eat2(Animal &al)
{
    al.eat();
}
int main()
{
    //堆区开辟
    Dog *d1=new Dog;
    Cat *c1=new Cat;
    animal_eat(d1);
    animal_eat(c1);
    //栈区定义
    Dog d2;
    Cat c2;
    animal_eat2(d2);
    animal_eat2(c2);
    return 0;
}

2.3 多态的原理

具有虚函数的类会存在一张虚函数表。这张表被当前类所有对象共用。每个类的对象内部都会有一个隐藏的虚函数指针成员,指向当前类的虚函数表。

在代码运行时,通过对象的虚函数指针找到对应虚函数表,在表中定位到虚函数的调用地址,执行对应的虚函数的内容。

因此使用多态会产生一些额外的开销。优点是代码编写更加的灵活高效,缺点是会降低代码的执行速度,代码可读性降低。

2.4 虚析构函数

(析构函数:当对象销毁时被调用,走完花括号“{}”,或delate释放堆空间)

如果不使用虚析构函数,且基类指针或引用指向派生类对象,使用delete销毁对象时,

析构函数不会被继承,但虚函数表会被继承,虚析构函数不会被覆盖,会自动生成一个新的对应派生类虚函数的虚析构函数,原虚析构函数对应基类虚函数。

只会触发基类的析构函数,如果在派生类中申请了内存资源,则会导致无法释放,出现内存泄漏的问题。

#include <iostream>

using namespace std;

class Animal
{
 public:
    // 虚函数
    virtual void eat()
    {
        cout << "动物爱吃饭" << endl;
    }
    ~Animal()
    {
        cout << "Animal 析构函数被调用了" << endl;
    }
};

class Dog:public Animal
{
public:
    void eat() override
    {
        cout << "狗爱吃骨头" << endl;
    }
    ~Dog()
    {
        cout << "Dog 析构函数被调用了" << endl;
    }
};

int main()
{
    Animal *al = new Dog;
    al->eat();//狗爱吃骨头
    delete al;//Animal 析构函数被调用了,只调用这一个
    return 0;
}

解决方法是给基类的析构函数使用virtual关键字修饰为虚析构函数,通过传递性可以把各个派生类的析构函数都变为虚析构函数。因此建议给一个可能为基类的类中的析构函数设置为虚析构函数。

#include <iostream>

using namespace std;

class Animal
{
 public:
    // 虚函数
    virtual void eat()
    {
        cout << "动物爱吃饭" << endl;
    }
    virtual ~Animal()
    {
        cout << "Animal 析构函数被调用了" << endl;
    }
};

class Dog:public Animal
{
public:
    void eat() override
    {
        cout << "狗爱吃骨头" << endl;
    }
    ~Dog()
    {
        cout << "Dog 析构函数被调用了" << endl;
    }
};

int main()
{
    Animal *al = new Dog;
    al->eat();//狗爱吃骨头,函数执行完后执行该函数对应的析构函数。
              //Dog 析构函数被调用了
    delete al;//Animal 析构函数被调用了
    return 0;
}

  1. 抽象类

如果基类只表达一些抽象的概念,并不与实际的对象相关联,这时候就可以使用抽象类。

如果一个类中有纯虚函数,这个类就是一个抽象类。

如果一个类是抽象类,则这个类中一定有纯虚函数。

纯虚函数是虚函数的一种,这种函数只有声明没有定义。不能实例化对象·

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

不能直接使用抽象类作为类型声明,因为不存在抽象类类型的对象。

抽象类作为基类时,具有两种情况:

  • 派生类继承抽象类,覆盖并实现所有的纯虚函数,此时派生类可以作为普通类使用,即不再是抽象类。
  • 派生类继承抽象类,没有把抽象类中所有的纯虚函数覆盖并实现,此时派生类也变为了抽象类,等待他的派生类覆盖并实现剩余的纯虚函数。(需要完全覆盖基类所有抽象函数

抽象类,无法实例化对象,但是可以创建指针和引用。

#include <iostream>
using namespace std;
// 抽象类:形状
class Shape
{
public:
    // 纯虚函数
    virtual void area() = 0; // 面积
    virtual void perimeter() = 0; // 周长
};
// 圆形
class Circle:public Shape
{
public:
    void area()
    {
        cout << "圆形计算面积" << endl;
    }
    void perimeter()
    {
        cout << "圆形计算周长" << endl;
    }
};
// 多边形
class Polygon:public Shape
{
public:
    void perimeter()
    {
        cout << "多边形计算周长" << endl;
    }
};
// 矩形
class Rectangle:public Polygon
{
public:
    void area()
    {
        cout << "矩形计算面积" << endl;
    }
};
int main()
{
//    Shape s; // 错误抽象类无法实例化对象
    Circle c;
    c.area();
    c.perimeter();
//    Polygon p; // 错误,没有完全覆盖基类抽象函数,抽象类无法实例化对象
    Rectangle r;
    r.area();
    r.perimeter();
    return 0;
}

使用抽象类注意以下几点:

  • 抽象类的析构函数必须是虚析构函数
  • 抽象类支持多态,可以存在指针或引用的声明格式
  • 因为抽象类的作用就是指定算法框架,因此在一个继承体系中,抽象类的内容相对丰富且重要。

2.5、纯虚析构(熟悉)

纯虚析构函数的定义:

纯虚析构的本质:是析构函数,作用是各个类的回收工作。而且析构函数不能被继承。

必须要为纯虚析构函数提供一个函数体。

纯虚析构函数,必须在类外实现。

#include <iostream>

using namespace std;

class Animal
{
public:
    virtual  void speak()
    {

    }
    Animal()
    {
        cout << "基类的构造函数被调用了" << endl;
    }
    // 纯虚析构
    virtual ~Animal() = 0;
};

// 纯虚析构类外实现
Animal::~Animal()
{
    cout << "基类析构函数被调用了" << endl;
}

class Dog:public Animal
{
public:
    void speak()
    {
        cout << "狗会汪汪汪" << endl;
    }
    Dog()
    {
        cout << "dog类构造函数被调用了" << endl;
    }

    ~Dog()
    {
        cout << "Dog类的析构函数被调用了" << endl;
    }
};

int main()
{

//    Animal al; // 错误基类是纯虚析构,无法实例化对象
    Animal *al = new Dog();
    al->speak();
    delete al;
    return 0;
}

虚析构与纯虚析构的区别:

  • 虚析构:virtual关键字修饰,有函数体,不会导致基类为抽象类。
  • 纯虚函数:virtual关键字修饰,结果=0,函数体需要类外实现,会导致基类是抽象类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1367345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第二百五十三回

文章目录 概念介绍使用方法示例代码 概念介绍 进度条是常用的组件之一&#xff0c;它主要用来显示某种动作的完成进度。Flutter提供了多种进度条组件&#xff0c;常用的是水平进度条&#xff1a;LinearProgressIndicator&#xff1b;圆形进度条 :CircularProgressIndicator和R…

4.4 TILING FOR REDUCED MEMORY TRAFFIC

我们在CUDA中使用设备内存方面有一个内在的权衡&#xff1a;全局内存大但速度慢&#xff0c;而共享内存小但速度快。一个常见的策略是将数据划分为称为tile的子集&#xff0c;以便每个tile都适合共享内存。tile一词”借鉴了一个类比&#xff0c;即大墙&#xff08;即全局内存数…

data-factory java 开源根据对象定义自动生成测试对象数据

创作目的 我们平时在写测试用例的时候&#xff0c;免不了要写一大堆 set 方法为对象设置属性。 有时候为了补全测试用例&#xff0c;这件事就会变得非常枯燥。 于是就在想&#xff0c;能不能写一个可以自动生成测试对象的工具呢&#xff1f; 于是就有了这一个没啥用的测试框…

网络调试 UDP1,开发板用动态地址-入门6

https://www.bilibili.com/video/BV1zx411d7eC?p11&vd_source109fb20ee1f39e5212cd7a443a0286c5 1, 开发板连接路由器 1.1&#xff0c;烧录无OS UDP例程 1.2&#xff0c;Mini USB连接电脑 1.3&#xff0c;开发板LAN接口连接路由器 2. Ping开发板与电脑之间通信* 2.1 根据…

人工智能AI网站大全—实现自动聊天、绘画、创作论文、生成视频等

人工智能正在逐步改变大家的生活和工作方式&#xff0c;本文总结当前人工智能实用网站&#xff0c;方便大家更快地把AI应用到工作和生活中&#xff0c;提高效率。主要包括自动聊天、自动创作论文、自动绘画、、自动创作视频等模块。 文章目录 Part1 10w.aiPart2 liblib.AIPart3…

洗地机什么牌子好?目前口碑最好的洗地机

如今&#xff0c;人们的生活中&#xff0c;洗地机已经成为了越来越受欢迎的清洁工具&#xff0c;洗地机能迅速而有效地清理地板、地毯以及其他硬表面&#xff0c;为用户提供更加方便快捷的洗地机体验。那么&#xff0c;洗地机什么牌子好?我们一起来看看目前口碑最好的洗地机有…

人工智能任务2-读懂Transformer模型的十个灵魂拷问问题,深度理解Transformer模型架构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能任务2-读懂Transformer模型的十个灵魂拷问问题&#xff0c;深度理解Transformer模型架构。Transformer模型是一种基于自注意力机制的神经网络架构&#xff0c;被广泛用于自然语言处理任务中&#xff0c;如机…

C语言注意点(4)

1、void *a是什么意思 答&#xff1a;泛型指针&#xff0c;但不规定其类型(就是地址确定&#xff0c;但数据长度不确定)在动态分配内存时&#xff0c;malloc的返回值就是该类型&#xff0c;方便用户进行强制转换。 2、VS怎么一键规范格式 for(i0;i<10;i)enter后&#xff0c;…

selenium3自动化测试(这一篇就够了)——自学篇

本人整理收藏了20年多家公司面试知识点整理 &#xff0c;以及学习路线和视频教程免费分享给大家&#xff0c;我认为对面试来说是非常有用的&#xff0c;想要资料的话请点1150305204暗号CSDN。或者点击文末名片进入&#xff0c;免费领取 &#xff08;一&#xff09;安装seleniu…

120°AGV|RGV小车激光障碍物传感器|避障雷达DE系列安装与连线方法

120AGV|RGV小车激光障碍物传感器|避障雷达DE系列包含DE-4211、DE-4611、DE-4311、DE-4511等型号&#xff0c;根据激光飞行时间&#xff08;TOF&#xff09;测量原理运行的&#xff0c;利用激光光束对周围进行 120 半径 4m&#xff08;90%反射率&#xff09;扫描&#xff0c;获得…

一文初步了解slam技术

本文初步介绍slam技术&#xff0c;主要是slam技术的概述&#xff0c;涉及技术原理、应用场景、分类、以及各自优缺点&#xff0c;和slam技术的未来展望。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;slam精进之…

基于Java SSM框架实现时间管理系统项目【项目源码+论文说明】

基于java的SSM框架实现时间管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于时间管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了时间管理…

小程序学习基础(页面加载)

打开首页&#xff0c;然后点击第一个按钮进去心得页面 进入心得页面以后 第一个模块是轮播图用的是swiper组件&#xff0c;然后就是四个按钮绑定点击事件&#xff0c;最后就是下拉刷新&#xff0c;下拉滚动&#xff0c;上拉加载。代码顺序wxml,js,wcss,json。 <!--pages/o…

04、Kafka ------ 各个功能的作用解释(Cluster、集群、Broker、位移主题、复制因子、领导者副本、主题)

目录 启动命令&#xff1a;CMAK的用法★ 在CMAK中添加 Cluster★ 在CMAK中查看指定集群★ 在CMAK中查看 Broker★ 位移主题★ 复制因子★ 领导者副本和追随者副本★ 查看主题 启动命令&#xff1a; 1、启动 zookeeper 服务器端 小黑窗输入命令&#xff1a; zkServer 2、启动 …

市场复盘总结 20240108

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 今日空仓 最常用的二种方法: 方法一:指标选股找强势股 select * from dbo.ResultAll where 入选类型 like %指标选股% and 入选日期=20240108;方法二…

【Flet教程】使用Flet以Python创建TODO应用程序

Flet是基于Python实现的Flutter图形界面GUI。除了使用Python&#xff0c;具备美观、简洁、易用&#xff0c;还有Flutter本身的跨平台&#xff08;安卓、iOS、Win、Mac、Web&#xff09;、高性能、有后盾的特点。下面是0.18版官方TODO APP教程&#xff0c;为了准确&#xff0c;保…

CentOS 多节点一键免密登录

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…

5 - 视图|存储过程

视图&#xff5c;存储过程 视图视图基本使用使用视图视图进阶 存储过程创建存储过程存储过程进阶存储过程参数循环结构 视图 视图是虚拟存在的表 表头下的数据在真表里 表头下的数据存储在创建视图时 在select命令访问的真表里 优点&#xff1a; 安全数据独立简单 用户无需关…

部署一款开源的交互审计系统—Next Terminal

博客地址 部署一款开源的交互审计系统—Next Terminal-雪饼 (xue6ing.cn)https://xue6ing.cn/archives/bu-shu-yi-kuan-kai-yuan-de-jiao-hu-shen-ji-xi-tong--next-terminal Next Terminal是什么&#xff1f; Next Terminal是一个开源的交互审计系统&#xff0c;具有以下主…

76.乐理基础-打拍子-二连音、四连音

内容来源于&#xff1a;三分钟音乐社 上一个内容&#xff1a;八三、八六拍的三角形打法-CSDN博客 这里要先理解了三连音的知识。 关于多少连音的总方针&#xff0c;其实就是两句话&#xff0c;如下图中的内容&#xff1a;二连音与四连音实际上就是下图中第二句话里的第一部分…