C++类与对象(下)

目录

    • 初始化列表
      • 单参数构造函数的隐式类型转换
    • static成员
    • 友元
      • 友元函数
      • 友元类
    • 内部类
    • 匿名对象
    • 了解:编译器优化
    • 练习题

初始化列表

构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    引用成员变量
    const成员变量
    自定义类型成员(且该类没有默认构造函数时)
class A
{
public:A(int a = 0):_a(a){}
private:int _a;
};class Date
{
public:// 初始化列表是每个成员定义的地方// 不管你写不写,每个成员都要走初始化列表Date(int year, int month, int day, int& i): _year(year), _month(month),_a(1),_refi(i){// 赋值_day = day;}void func(){++_refi;++_refi;}private:int _year;  // 每个成员声明int _month = 1;int _day = 1;// C++11支持给缺省值,这个缺省值给初始化列表// 如果初始化列表没有显示给值,就用这个缺省值// 如果显示给值了,就不用这个缺省值// 以下必须定义时初始化const int _x = 10;int& _refi;A _a;
};

初始化列表是构造函数的一部分
初始化列表是每个成员定义的地方
不管你写不写,每个成员都要走初始化列表
在构造函数的初始化列表阶段,如果不写,对内置类型用随机值去初始化(看编译器,有些编译器会处理成0 ),对于自定义类型,会去调用它的默认构造
初始化列表初始化的顺序跟声明的顺序有关

能用初始化列表就用初始化初始化列
有些场景还是需要初始化列表和函数体混着用

Stack(size_t capacity):_array((DataType*)malloc(sizeof(DataType) * capacity)),_size(0),_capacity(capacity)
{cout << "Stack()" << endl;if (NULL == _array){perror("malloc申请空间失败!!!");return;}memset(_array, 0, sizeof(DataType) * _capacity);
}

A aa1(1);是可以的,还有一种写法:

单参数构造函数的隐式类型转换

单参数
private:int _a;

int可以转换为A,支持隐式类型转换
用2调用A构造函数生成一个临时对象,再用这个对象去拷贝构造aa2
编译器会再优化,优化用2直接构造
A aa2 = 2;

证明:
A& ref1 = 2;//这样写不行
原因是中间生成临时对象,临时对象具有常性,所以ref不可以引用这个临时对象,这是权限的放大,变成 const A& ref1 = 2;就可以了

如果不想让隐式类型发生,加explicit关键字

explicit A(int i):_a(i){cout << "A(int i)" << endl;}

在这里插入图片描述
如果是多参数:

C++11 支持多参数的隐式类型转换B bb1(1, 1);B bb2 = { 2, 2 };const B& ref2 = { 3,3 };

匿名对象:

    // 有名对象 特点:生命周期在当前局部域A aa6(6);// 匿名对象。特点:生命周期只在这一行A(7);

想只使用一次对象时可以用匿名对象,可以节省代码量

	A aa7(7);s.PushBack(aa7);s.PushBack(A(8));
Solution sl;
sl.Sum_Solution(10);Solution().Sum_Solution(100);//Solution()定义了一个没有名字的对象
	Date d1(2023, 7, 28);cout << d1;cout << Date(2023, 7, 28);

static成员

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

实现一个类,计算程序中创建出了多少个类对象。

// 累积创建了多少个对象  
int n = 0;// 正在使用的还有多少个对象
int m = 0;class A
{
public:A(){++n;++m;}A(const A& t){++n;++m;}~A() {--m;}
private:
};//A& Func(A& aa)
A Func(A aa)
{return aa;
}int main()
{A aa1;A aa2;cout << n << " " << m << endl;// 可能被外面随意修改//--n;//++m;A();cout << n << " " << m << endl;Func(aa1);cout << n << " " << m << endl;return 0;
}
class A
{
public:A(){cout << "A()" << endl;++n;++m;}A(const A& t){++n;++m;}~A(){--m;}private:// 静态成员变量属于所有A对象,属于整个类// 声明// 累积创建了多少个对象  static int n;// 正在使用的还有多少个对象static int m;
};
//定义
int A::n = 0;
int A::m = 0;

不是单单属于某个对象,而是想属于所有A对象,属于整个类
缺省值是在初始化列表阶段使用的,但是静态成员变量不会走初始化列表,初始化列表是某个对象的成员的初始化,静态成员变量属于所有对象,所以不能给缺省值。

这两个静态成员不存在对象里面,存在静态区
因为类里面是声明,所以静态成员要在外面定义,相当于声明和定义分离,不是在类外面访问,在.h里定义会出问题

n和m是公有情况下
int main()
{A aa1;A aa2;cout << A::n << " " << A::m << endl;cout << aa1.n << " " << aa2.m << endl;A* ptr = nullptr;cout << ptr->n << " " << ptr->m << endl;// 可能被外面随意修改A();cout << n << " " << m << endl;Func(aa1);cout << n << " " << m << endl;return 0;
}

n、m属于整个类,可以用作用域限定符去拿到
从底层角度看,n和m不在aa1、aa2、ptr指向的对象里面,在静态区,ptr->n这种方式只是帮助其突破类域,虽然是空指针但是不会报错,有没有解引用要看数据在哪。

如果n和m是私有那上面三种方法都访问不了,可以通过成员函数访问,公有和函数去找m不同的是公有的m可以被修改,函数的方法一般不会去修改m,想修改可以返回引用修改。

int GetM()
{return m;
}void Print()
{cout << m <<" " << n << endl;
}

但调用成员函数需要创建对象
用匿名对象调用函数,也会多创建一个对象出来,干扰逻辑
想不创建对象也能访问——静态成员函数

//静态成员函数的特点:没有this指针
static int GetM()
{return m;
}// ...static void Print()
{// x++; // 不能访问非静态,因为没有thiscout << m <<" " << n << endl;
}

以前需要对象去调用,是因为需要去类里找还有就是要传this指针

A::Print();现在不需要传this指针了,只需要突破类域就可以

静态成员函数不能访问非静态成员,因为没有this指针

小总结:

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以
友元不宜多用。
友元分为:友元函数和友元类

友元函数

class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
};

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在
类的内部声明,声明时需要加friend关键字。

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰(没有this指针)
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

  • 友元关系是单向的,不具有交换性。
    比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
    访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

  • 友元关系不能传递
    如果C是B的友元, B是A的友元,则不能说明C时A的友元。

class Time
{friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
}

内部类

1、B类受A类域和访问限定符的限制,其实他们是两个独立的类
2、内部类默认就是外部类的友元类

class A
{//public:class B{public:void FuncB(int year){A aa;year=aa.c;_d++;}private:int _b;};void func(){B bb;//bb._b = 1;//这样是不行的}private:const static int _a;static int _d;int c = 2;
};const int A::_a = 1;
int A::_d = 3;int main()
{cout << sizeof(A) << endl;A aa;//A::B bb1;//访问不了return 0;
}
  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
    (非static成员访问需要对象)。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。

匿名对象

    // 有名对象 特点:生命周期在当前局部域A aa6(6);// 匿名对象。特点:生命周期只在这一行A(7);

想只使用一次对象时可以用匿名对象,可以节省代码量

	A aa7(7);s.PushBack(aa7);s.PushBack(A(8));
Solution sl;
sl.Sum_Solution(10);Solution().Sum_Solution(100);//Solution()定义了一个没有名字的对象
	Date d1(2023, 7, 28);cout << d1;cout << Date(2023, 7, 28);

匿名对象、临时对象具有常性

void f1(const A& aa = A())
{aa.Print();
}int main()
{A aa1;f1(aa1);f1(A(1));f1(2);f1();A();// const引用会延长匿名对象声明周期// ref出了作用域,匿名对象就销毁了const A& ref = A();A aa2;return 0;
}

匿名对象、临时对象具有常性,不可以修改
所以不能直接用引用接收,应该在引用前在加const才可以

const引用同时可以延长匿名对象的生命周期

了解:编译器优化

C++并没有规定要优化,不过现在大多数编译器都会优化

int main()
{A aa1;f1(aa1);cout << "--------------------------" << endl;// 一个表达式,连续的步骤里面,连续的构造会被合并f1(A(1));cout << "--------------------------" << endl;f1(1);cout << "--------------------------" << endl;A aa2 = 1;cout << "--------------------------" << endl;A aa3 = A(2);return 0;
}

在这里插入图片描述

本来一次构造,两次拷贝构造

在函数返回之前,本来要去拷贝临时对象,编译器优化之后

在这里插入图片描述
ret2没有优化的原因是:
第一点:同类型才能优化,比如拷贝构造和构造都是构造才能优化,拷贝构造和赋值不能合并
第二点:在两个步骤里,不在同一个步骤里

在这里插入图片描述

要构造又要拷贝构造,又在同一个步骤里,就优化成直接构造ret

在这里插入图片描述
了解这些是让我们知道怎样写能触发编译器的优化,更高效

练习题

求和OJ题
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

class Sum{
public:Sum(){_m+=_n;_n++;}static int GetM(){return _m;}
private:
static int _n;
static int _m;
};
int Sum:: _n=1; 
int Sum:: _m=0; class Solution {
public:int Sum_Solution(int n) {Sum a[n];return Sum::GetM();}
};

改进:

class Solution {
public:class Sum{public:Sum(){_m+=_n;_n++;}};int Sum_Solution(int n) {Sum a[n];return _m;}
private:
static int _n;
static int _m;
};int Solution:: _n=1; 
int Solution:: _m=0;

如果不用内部类,得排除一次构造函数带来的干扰:

class Solution {
public:// class Sum{// public://     Sum()//     {//         _m+=_n;//         _n++;//     }// };Solution(){_m+=_n;_n++;}int Sum_Solution(int n) {Solution a[n];return _m;}
private:
static int _n;
static int _m;
};int Solution:: _n=0; 
int Solution:: _m=0; 

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

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

相关文章

【论文】【生成对抗网络五】Wasserstein GAN (WGAN)

【题目、作者】&#xff1a; 紫色&#xff1a;要解决的问题或发现的问题 红色&#xff1a;重点内容 棕色&#xff1a;关联知识&#xff0c;名称 绿色&#xff1a;了解内容&#xff0c;说明内容 论文地址&#xff1a; 论文下载 本篇文章仅为原文翻译&#xff0c;仅作参考。…

jar命令的安装与使用

场景&#xff1a; 项目中经常遇到使用WinR软件替换jar包中的文件&#xff0c;有时候存在WinRAR解压替换时提示没有权限&#xff0c;此时winRAR不能用还有有什么方法替换jar包中的文件。 方法&#xff1a; 使用jar命令进行修改替换 问题&#xff1a; 执行jar命令报错jar 不…

[用go实现解释器]笔记1-词法分析

本文是《用go实现解释器》的读书笔记 ​ https://malred-blog​malred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 ​github.com/malred/malanghttp:/…

Ubuntu网络设置之固定IP详解

尊敬的家人们&#xff0c;欢迎观看我的文章&#xff01;今天&#xff0c;我们将为您介绍Ubuntu22.04操作系统中固定IP的设置方法&#xff0c;帮助您更好地管理网络连接并提高网络稳定性。 什么是固定IP&#xff1f; 在网络中&#xff0c;IP地址是设备在网络上的唯一标识。通常…

Linux6.31 Kubernetes 二进制部署

文章目录 计算机系统5G云计算第二章 LINUX Kubernetes 部署一、二进制搭建 Kubernetes v1.201.操作系统初始化配置2.部署 etcd 集群3.Kubernetes 集群架构与组件4.部署 Master 组件5.部署 Worker Node 组件6.部署 CNI 网络组件——部署 flannel1&#xff09;K8S 中 Pod 网络通信…

lc1074.元素和为目标值的子矩阵数量

创建二维前缀和数组 两个for循环&#xff0c;外循环表示子矩阵的左上角&#xff08;x1,y1&#xff09;&#xff0c;内循环表示子矩阵的右下角&#xff08;x2,y2&#xff09; 两个for循环遍历&#xff0c;计算子矩阵的元素总和 四个变量&#xff0c;暴力破解的时间复杂度为O(…

招投标系统简介 企业电子招投标采购系统源码之电子招投标系统 —降低企业采购成本 tbms

​功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

CSS学习记录(基础笔记)

CSS简介: CSS 指的是层叠样式表* (Cascading Style Sheets)&#xff0c;主要用于设置HTML页面的文字内容&#xff08;字体、大小、对齐方式&#xff09;&#xff0c;图片的外形&#xff08;边框&#xff09; CSS 描述了如何在屏幕、纸张或其他媒体上显示 HTML 元素 CSS 节省…

Pytorch使用VGG16模型进行预测猫狗二分类

目录 1. VGG16 1.1 VGG16 介绍 1.1.1 VGG16 网络的整体结构 1.2 Pytorch使用VGG16进行猫狗二分类实战 1.2.1 数据集准备 1.2.2 构建VGG网络 1.2.3 训练和评估模型 1. VGG16 1.1 VGG16 介绍 深度学习已经在计算机视觉领域取得了巨大的成功&#xff0c;特别是在图像分类任…

ubuntu调整路由顺序

Ubuntu系统跳转路由顺序 1、安装ifmetric sudo apt install ifmetric2、查看路由 route -n3、把Iface下面的eth1调到第一位 sudo ifmetric eth1 0命令中eth1是网卡的名称&#xff0c;更改网卡eth1的跃点数&#xff08;metric值&#xff09;为0&#xff08;数值越小&#xf…

prometheus监控k8s kube-proxy target down

prometheus kube-proxy target down 解决 修改配置 kubectl edit cm/kube-proxy -n kube-systemmetricsBindAddress: "0.0.0.0:10249"删除 kube-proxy pod 使之重启应用配置 kubectl delete pod --force `kubectl get pod -n kube-system |grep kube-proxy|awk {pr…

深度学习Redis(3):主从复制

前言 在前面的两篇文章中&#xff0c;分别介绍Redis内存模型和Redis持久化 在Redis的持久化中曾提到&#xff0c;Redis高可用的方案包括持久化、主从复制&#xff08;及读写分离&#xff09;、哨兵和集群。其中持久化侧重解决的是Redis数据的单机备份问题&#xff08;从内存到…

VR实景导航——开启3D可视化实景导航新体验

数字化时代&#xff0c;我们大家出门在外都是离不开各种导航软件&#xff0c;人们对导航的需求也越来越高&#xff0c;而传统的导航软件由于精度不够&#xff0c;无法满足人们对真实场景的需求&#xff0c;这个时候就需要VR实景导航为我们实景指引目的地的所在。 VR实景导航以其…

使用Kmeans算法完成聚类任务

聚类任务 聚类任务是一种无监督学习任务&#xff0c;其目的是将一组数据点划分成若干个类别或簇&#xff0c;使得同一个簇内的数据点之间的相似度尽可能高&#xff0c;而不同簇之间的相似度尽可能低。聚类算法可以帮助我们发现数据中的内在结构和模式&#xff0c;发现异常点和离…

【Linux】进程信号

文章目录 一、信号入门1. 生活角度的信号2. 技术应用角度的信号3. Linux下常见的信号 二、信号产生1. 终端按键产生信号2. 核心转储3. 通过系统调用向进程发信号4. 软件条件产生信号5. 硬件异常产生信号 三、信号保存1. 信号相关概念及内核中的信号表示2. 信号集操作函数3. sig…

java 定时任务不按照规定时间执行

这里写目录标题 使用异步启动可能出现的问题排查代码中添加的定时任务步骤是否正确排查是否任务阻塞&#xff0c;如果定时任务出现异常阻塞后&#xff0c;将不会在次执行java中多个Scheduled定时器不执行为了让Scheduled效率更高&#xff0c;我们可以通过两种方法将定时任务变成…

OLED拼接屏:在广告牌领域的革命性应用,显示、效果

OLED透明屏是一种新型的显示技术&#xff0c;它具有高亮度、高对比度、快速响应和低功耗等优点&#xff0c;可以在透明的基底上显示图像和视频。 OLED透明屏的出现&#xff0c;为我们的生活带来了许多新的可能性。 首先&#xff0c;OLED透明屏可以应用于智能手机和平板电脑等移…

android kernel移植5-RK3568

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言1.添加开发板默认配置文件前言 前面我们已经学会了移植uboot,其实就是把瑞芯微的关于uboot的一些文件的名字和编译指定的文件改为自己定义的问价和名字,那么接下来的Android kernel其实也是…

快速转换PDF文件: Python和PyMuPDF教程

解决问题 有时候将文档上传Claude2做分析&#xff0c;有大小限制&#xff0c;所以需要切割pdf文档为几个小点的文档&#xff0c;故才有了本文章。 如何用Python和PyMuPDF制作你想要大小的PDF&#xff1f; PDF是一种广泛使用的文件格式&#xff0c;可以在任何设备上查看和打印…

CentOS7.3 安装 docker

亲测、截图 阿里云服务器 文章目录 更新源2345 启动开机自启 更新源 sudo yum update -y2 sudo yum install -y yum-utils device-mapper-persistent-data lvm23 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo4 sudo yum …