9、类和对象

9.1 封装

9.1.1 封装的例子

class Student {
public:string name;int age;
public:void setName(string name_) {name = name_;}
};
int main() {Student s1;s1.setName("zhangsan");return 0;
}

类中的行为都叫做成员,例如成员属性,成员变量,成员方法

9.1.2 访问权限

名称权限范围
public类内、类外都可以访问
protected类内可以访问,类外不可以访问,子类可以访问
private类内可以访问,类外不可以访问,子类不可以访问

strcut 和class 区别

  • 默认权限不同,struct 默认权限为public,class默认权限为private

9.2 对象的初始化和清理

9.2.1 构造函数与析构函数

构造函数语法: 类名(){}

  • 没有返回值也不需要用void修饰
  • 函数名与类名相同
  • 可以有参数,可以重载

析构函数语法: ~类名(){}

  • 没有返回值也不需要用void修饰
  • 函数名与类名相同
  • 可以有参数,不可以重载
  • 在对象销毁前会自动调用析构函数

9.2.2 构造函数的分类及调用

分类:

  • 按照参数:有参构造函数和无参构造函数
  • 按照类型:拷贝构造函数和普通构造函数
// 拷贝构造函数
Person(const Person &p){age=p.age;name=p.name;
}

调用:

  • 括号法:
    • 无参构造函数调用:Person p1;
    • 有参构造函数调用:Person p2(10);
    • 拷贝构造函数调用:Persiong p2(p1);
  • 显示法:
    • Person p1;
    • Person p2 = Person(10);
    • Person p3 = Person(p2);
  • 隐式法
    • Person p1=10;// 相当远Person p1=Person(10);
    • Persion p5=p4;

c++ 默认提供无参构造函数,无参析构函数以及拷贝函数

9.2.3 初始化列表

class Student {
public:string name;int age;Person():name("zhangsan"),age(10){}Person(string a,int b):name(a),age(b){}
public:void setName(string name_) {name = name_;}
};

9.2.4 类对象作为类成员

  • 当类对象作为类成员的时候,类成员的构造方法先执行,即内对象先构造,在构造外对象
  • 析构方法与构造方法相反

9.2.5 静态成员

使用static修饰的成员变量和成员函数称之为静态成员变量、静态成员函数

  • 静态成员变量:

    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数:

    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

静态成员变量

  1. 初始化
class Student {public:string name;int age;static string school;
public:void setName(string name_) {name = name_;}
};
int deliverValue(Student s);
int deliverPlace(Student* s);
string Student::school = "黑龙江";// 静态成员变量初始化
int main() {Student s1;s1.setName("zhangsan");s1.school = "哈尔滨";// 或者直接通过某个对象初始化也可以return 0;
}
  1. 访问方式
  • 通过对象访问 s1.school
  • 通过类名访问 Student::school

静态成员函数

  1. 访问方式
  • 通过对象访问 s1.func();
  • 通过类名访问 Student::func();

9.3 C++对象模型和this指针

9.3.1 this指针

成员变量和成员函数式分开存储的,静态成员变量,静态成员函数,以及成员函数都是共享的,只有非静态成员变量是对属于对象的。
那么对于非静态成员函数是怎么区分是哪个对象调用自己的?C++通过提供特殊的对象指针this指针解决上述问题。this指针指向被调用的成员函数所属的对象。

  • this 指针隐含在每个非静态成员函数内部,不需要定义,直接可以使用

this指针的作用

  • 当形参与成员变量同名时,可以用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可以使用 return *this
class Student {public:string name;int age;static string school;
public:void setName(string name) {this->name = name;}
};

在VS2022中键盘输入this.age= 会自动修正为this->age=,对于VS2022 指针的"."相当于“->”的快捷键。

9.3.2 const修饰成员函数

  • 常函数
    • 成员函数添加const后我们称这个函数为常函数
    • 常函数内不可以修改成员属性
    • 成员属性声明时加关键字multable后,在常函数中依然可以修改
  • 常对象
    • 声明对象前加const称该对象为常对象
    • 常对象只能调用常函数

常函数如下:

void show(string name) const {cout<<this->name;
}

const 本质上是修饰的this指针

9.3 友元

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

9.3.1 全局函数做友元

在类中加入friend修饰的函数声明,就可以将该函数定义为当前类的友元

class Student {friend void visit(Student* s);
public:string name;int age;static string school;
private:string score;
public:void setName(string name) {this->name = name;}void show(string name) const {cout<<this->name;}
};
void visit(Student* s) {cout << s->score;
}

9.3.2 类做友元

在类中加入friend修饰的类声明,就可以将该类定义为当前类的友元

9.3.2 类做友元

在类中加入friend修饰的类声明,就可以将该类定义为当前类的友元

friend Person p;

9.3.2 成员函数做友元

在类中加入friend修饰的成员函数声明,就可以将成员函数定义为当前类的友元

	friend void GoodGay::visit();

9.4 运算符重载

9.4.1 对于加号进行重载

  • 成员函数重载
class Person(){Person operator+(Person &p){}
}
// 使用的时候
Person p3=p1.operator+(p2);
// 或者
Person p3=p1+p2;
  • 全局函数重载
Person operator+(Person &p1, Person &p2){}
// 使用的时候
operator+(p1,p2)
// 或
Person p3=p1+p2;

9.4.2 对左移运算符进行重载

在输出的时候cout<< 无法对自定义类进行输出,所以可以对<<进行重载
通常情况下不会使用成员函数重载<<。因为无法简化为cout<<p。

  • 使用全局函数重载<<
ostream& operator<<(ostream &cout,Person &p){cout<<p.name;rerturn cout;
}

9.4.3 递增运算符重载

  • 前置递增重载(成员函数重载)
MyInteger& operator++(){m_num++;return *this;
}

这里必须要返回引用,当不返回引用的时候,两次连续的递增操作会出现问题

MyInteger a(0);// a.m_num=0;
cout<<++(++a);// 输出2  cout 重载过的函数,输出的是a.m_num的值
cout<<a;//输出1 

这是因为如果不返回引用,那么会第一次++a会返回一个a的副本,即匿名对象,然后第二次递增是对于这个副本进行递增,因此递增的结果是正确的,但是真正的a.m_num只递增了一次

  • 后置递增重载
MyInteger& operator++(int){
MyInteger tmp=*this;m_num++;return tmp;
}

这里需要注意,后置递增两次连加本身就是无法加2的,因为后置递增是先返回原值,在执行加法,即普通的int a=0;(a++)++也是等于1,在vs2022会直接报错,显示“表达式必须是可修改的左值”

9.4.4 赋值运算符重载

Person& operator=(Person &p){// 要先对原本对象存在堆区里的数据进行释放,否则就会存在一直不释放的数据if(m_Age!=NUll){delete m_Age;m_Age=NULL;}m_Age= new int*p.m_Age);return *this;
}

9.4.5 关系运算符重载

Person& operator==(Person &p){if(m_Age==p.m_Age){return true;}return false;
}

9.4.6 函数调用运算符重载(仿函数)

class MyAdd{
public:void operator()(String s){cout<<s<<endl;}
}void test(){MyAdd myadd;int ret = myadd(100,100);cout<<ret<<endl;
}

9.5 继承

9.5.1 继承

class 子类:继承方式 父类
(子类也叫基类,父类也叫基类)

9.5.2 继承方式

  • public:权限不变,即父类中的私有还是私有不可访问,受保护的还是受保护的,可以访问
  • protected:父类中的公共权限和保护权限在子类中全部变为保护权限,私有不可访问
  • private:公共权限和保护权限都变为私有权限,私有不可访问

9.5.3 继承中的对象

在父类中的任何非静态属性都会在子类中存在一份,只不过是父类的私有属性,无法访问

9.5.4 继承中的构造和析构顺序

base构造
son构造
son析构
base析构

9.5.5 继承中同名成员访问

class Base{publicBase(){m_A=100;}
}class Son :public Base{publicSon(){m_A=200;}
}int main(){Son s;cout<<"son"<<s.m_A<<endl;cout<<"Base"<<s.Base::m_A<<endl;
}

成员函数的调用方法和成员属性的调用方法类似

注意:当子类和父类中出现同名成员函数时,父类中的所有同名成员函数被隐藏,即子类中出现change()函数,那么父类中的change(),change(int a)等等都会被隐藏,一定要调用的时候加父类作用域。

9.5.6 多继承

class 子类:继承方式 父类1,继承方式 父类2

在实际开发中通常不建议使用多继承,父类属性出现重名情况是每次调用都需要明确作用域

9.5.6 菱形继承

在这里插入图片描述

  • 两个父类存在相同的属性,可以通过加作用域区分 s.Sheep:m_Age; s.Tuo:m_Age;
  • 存在重复属性,造成了数据的冗余,浪费内存。利用虚继承来解决:class Sheep:virtual public Animal;class Tuo:virtual public Animal; 这种情况下,s.Sheep:m_Age; s.Tuo:m_Age;是同一份数据

9.6 多态

9.6.1 基本概念

  • 静态多态:函数重载、运算符重载以及复用函数名都属于静态重载
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态的根本区别:

  • 静态多态的函数地址早绑定,编译阶段确定函数地址
  • 动态多态的函数地址晚绑定,运行阶段确定函数地址
class Animal{
public:virtual void speack(){// 如果不使用虚函数,那么test函数执行的时候无论输入的是小猫,小狗都是“动物在说话”cout<<"动物在说话";}
}
class Catpublic Animal{
public:void speack(){cout<<"猫在说话";}
}
void test(Animal &animal){animal.speak();
}

动态多态满足条件:

  • 有继承关系
  • 子类重写父类的虚函数

重写:函数名与参数完全相同

在这里插入图片描述

9.6.2 纯虚函数和抽象类

  • 语法:virtual 返回值类型 函数名(参数列表)= 0;
  • 当类中存在纯虚函数那么这个类被称为抽象类
  • 抽象类特点:
    • 抽象类的子类必须重写纯虚函数。
    • 抽象类无法实例化对象

9.6.3 虚析构和纯虚析构

问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,会造成内存溢出
解决办法:将父类中的析构函数改成虚析构或者纯虚析构

父类的纯虚析构代码要在类外实现


Animal::~Animal(){}

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

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

相关文章

Spring Cloud全解析:负载均衡算法

负载均衡算法 集中式负载均衡 在服务的消费方和提供方之间使用独立的LB设施(可以是硬件&#xff0c;如F5&#xff0c;也可以是软件&#xff0c;如Nginx)&#xff0c;由该设施负责把访问请求通过某种策略转发至服务的提供方 进程内负载均衡 将LB逻辑集成到消费方&#xff0c…

Redis篇 - 深入了解查询缓存与缓存带来的问题

引言 在现代Web应用程序中&#xff0c;为了提高数据访问速度和减轻数据库的压力&#xff0c;缓存技术变得越来越重要。Redis作为一款高性能的键值存储系统&#xff0c;在缓存领域有着广泛的应用。然而&#xff0c;随着缓存的引入&#xff0c;一系列新的挑战也随之而来。本文将…

飞速(FS)S5800-48T4S:如何使用MLAG?

MLAG&#xff08;多机箱链路聚合组&#xff09;可实现无缝故障转移并优化带宽利用率&#xff0c;从而增强网络冗余和提高可扩展性。它允许多台交换机作为一个统一实体运行&#xff0c;从而降低停机风险并确保网络运行不中断。飞速&#xff08;FS&#xff09;S5800-48T4S是一款支…

IP学习——Fiveday

设备排错 [R1]display ip interface brief 查看路由器接口的IP地址信息 [R1]display current-configuration int g0/0/1.10 查看路由器接口的IP地址信息 TG---> trunk查看vlan指令:displayvan其中UT--->accessc.vlan确认完成后 即链路层配置完成排查网络层错误 排查终端主…

二维高斯函数的两种形式

第一种形式很常见 多元正态分布 多元正态分布&#xff08;Multivariate Normal Distribution&#xff09;&#xff0c;也称为多变量正态分布或多维正态分布&#xff0c;是统计学中一种重要的概率分布&#xff0c;用于描述多个随机变量的联合分布。 假设有 n n n 个随机变量…

Monkey日志ANR、CRASH、空指针异常及其他异常数据分析

引言 在Android开发过程中&#xff0c;monkey测试是一种常用的随机测试手段&#xff0c;用于模拟用户的各种操作来发现应用中的稳定性问题。通过monkey测试生成的日志文件包含了丰富的信息&#xff0c;包括应用程序崩溃&#xff08;Crash&#xff09;、无响应&#xff08;ANR&…

【LabVIEW学习篇 - 18】:人机界面交互设计02

文章目录 错误处理函数简单错误处理器通用错误处理器清楚错误合并错误错误代码至错误簇转换查找第一个错误 鼠标指针 错误处理函数 在LabVIEW中&#xff0c;是通过错误输入簇和错误输出簇来传递错误信息&#xff0c;可以将底层错误信息传递到上层VI。设计人员需要对不同程度的…

2024 数学建模高教社杯 国赛(C题)| 农作物的种植策略 | 建模秘籍文章代码思路大全

铛铛&#xff01;小秘籍来咯&#xff01; 小秘籍团队独辟蹊径&#xff0c;运用等多目标规划等强大工具&#xff0c;构建了这一题的详细解答哦&#xff01; 为大家量身打造创新解决方案。小秘籍团队&#xff0c;始终引领着建模问题求解的风潮。 抓紧小秘籍&#xff0c;我们出发…

过滤器(Filter)和拦截器(Interceptor)

在Web开发中&#xff0c;过滤器&#xff08;Filter&#xff09;和拦截器&#xff08;Interceptor&#xff09;都是重要的组件&#xff0c;它们都可以对HTTP请求进行预处理、后处理以及一些额外的操作。然而&#xff0c;它们之间在多个方面存在明显的区别 1. 运行位置 过滤器&…

在python项目的docker镜像里使用pdm管理依赖

前言 在 DjangoStarter 项目中&#xff0c;我已经使用 pdm 作为默认的包管理器&#xff0c;不再直接使用 pip 所以部署的时候 dockerfile 和 docker-compose 配置也得修改一下。 dockerfile 首先修改一下 dockerfile ARG PYTHON_BASE3.11FROM python:$PYTHON_BASE# 设置 pytho…

高并发内存池(一):项目介绍与定长内存池的实现

目录​​​​​​​ 项目介绍 池化技术 内存池 内存碎片 malloc工作原理 定长内存池 申请内存 释放内存 定位new VirtualAlloc函数 封装VirtualAlloc 定长内存池的最终代码 项目介绍 项目原型&#xff1a;goole的开源项目tcmalloc&#xff08;Thread-Caching Mal…

9.5 面试题

1、多继承下&#xff0c;地址转换问题&#xff1a; 在 C 中&#xff0c;如果类 C 是多继承自 A 和 B&#xff0c;在执行强制类型转换时&#xff0c;地址值是否发生改变&#xff0c;取决于内存布局和继承方式。具体来说&#xff1a; 1. 标准布局下&#xff08;无虚继承&#x…

CIOE中国光博会&电巢科技即将联办“智能消费电子创新发展论坛”

在科技浪潮汹涌澎湃的当下&#xff0c;从通信领域的高速光传输&#xff0c;到消费电子中的高清显示与先进成像技术&#xff0c;光电技术的应用范围不断拓展且日益深化。而AIGC 凭借其丰富的内容供给与个性化反馈能力&#xff0c;正为新一代消费电子及智能穿戴产品开辟崭新的发展…

前端请求的路径baseURL怎么来的 ?nodejs解决cors问题的一种方法

背景&#xff1a;后端使用node.js搭建&#xff0c;用的是express 前端请求的路径baseURL怎么来的 &#xff1f; 前后端都在同一台电脑上运行&#xff0c;后端的域名就是localhost&#xff0c;如果使用的是http协议&#xff0c;后端监听的端口号为3000&#xff0c;那么前端请求…

#include <iostream>介绍

在C编程中&#xff0c;#include <iostream> 是一个非常重要的预处理指令&#xff0c;用于引入输入输出流库。以下是对 #include <iostream> 的详细介绍&#xff1a; 1. 作用 #include <iostream> 指令的主要作用是使程序能够使用标准输入输出流的功能。它包…

S3C2440开发板:时钟,PWM定时器控制蜂鸣器发声

时钟 时钟和电源管理模块由三部分组成&#xff1a;时钟控制&#xff0c;USB 控制和电源控制。 S3C2440A 中的时钟控制逻辑可以产生必须的时钟信号&#xff0c;包括 CPU 的 FCLK&#xff0c;AHB 总线外设的 HCLK 以及 APB 总线外设的 PCLK。S3C2440A 包含两个锁相环&#xff08…

VBA进行excel坐标转换

在Excel里利用坐标绘图时&#xff0c;可以比较容易想到采用数据透视表&#xff0c;但是数据透视表生成的图不可更改&#xff0c;因此本案例采用VBA进行坐标变换而不改变原始值来转换图像&#xff0c;即实现图像的左右翻转和上下翻转&#xff0c;如下图所示&#xff0c;选择map的…

redis的持久化RDB和AOF

redis的持久化有RDB和AOF两种方式。RDB(Redis Database)是redis在指定间隔将当前时间点的内存数据快照保存成一个二级制rdb文件。AOF(Append Only File)是将每一条写指令写入到特定日志文件&#xff0c;然后当服务重启时通过回放这些指令来恢复原来的数据集。redis可以同时开启…

fastadmin 文件上传腾讯云

1-安装腾讯云SDK composer require qcloud/cos-sdk-v5 2-腾讯云配置 <?phpnamespace app\common\controller;use Qcloud\Cos\Client; use think\Controller; use think\Db;class Tencent extends Controller {/*** 上传文件* param $config* param $key* return array*/p…

Linux下快速判断当前终端使用的是bash or csh

在Linux下设置环境变量的时候&#xff0c;可能你也遇到过export: Command not found一类的错误。这是因为当前终端使用的不是bash&#xff0c;如何快速判断当前终端使用的是哪种类型的shell呢&#xff1f; echo $0判断shell类型 最简单的方法就是在终端输入echo $0&#xff0…