C++中类和对象其他内容

目录

explicit关键字

static成员

static成员的介绍

static成员的使用

友元

友元函数

友元类

内部类

匿名对象


explicit关键字

在C++中,给类对象初始化时会调用类的构造函数,但是也可以使用赋值运算符为构造函数只有一个参数(或者只有一个参数没有缺省值)的类对象赋值,如下面代码


#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){}void print(){cout << _num << endl;}
};int main()
{test t(1);//直接调用构造函数进行对象实例化test t1 = 1;t.print();t1.print();return 0;
}
输出结果:
1
1

在上面的代码中,test类对象初始化时需要调用有一个参数的构造函数(对应test t(1)),而也可以直接使用赋值运算符,将初始化值赋值给类对象,这个过程经历了:调用构造函数使用整型1为临时对象初始化,再调用拷贝构造函数将临时对象拷贝给t1对象,这个过程也是一种类型转换,但是实际上这个过程一般会被编译器优化为直接调用构造函数,使用整型1对类对象初始化,即优化过程:构造函数+拷贝构造函数->构造函数

而如果不想以上面的方式,只用直接调用构造函数的方式对类对象进行初始化时,可以使用explicit关键字对构造函数进行修饰

#include <iostream>
using namespace std;class test
{
private:int _num;
public:explicit test(int num):_num(num){}void print(){cout << _num << endl;}
};int main()
{test t(1);//直接调用构造函数进行对象实例化test t1 = 1;t.print();t1.print();return 0;
}
报错信息:
不存在从 "int" 转换到 "test" 的适当构造函数

当构造函数被explicit关键字修饰后,test t1 = 1的初始化方式失效

在C++11标准规范中,也支持对不只有一个参数的构造函数使用对类对象进行赋值初始化的方式

 
#include <iostream>
using namespace std;class test
{
private:int _num;int _num1;
public:test(int num, int num1):_num(num),_num1(num1){}void print(){cout << _num << " " << _num1 << endl;}
};int main()
{test t(2, 3);test t1 = { 2,4 };t.print();t1.print();return 0;
}
输出结果:
2 3
2 4

同样,如果不愿意使用直接赋值的方式为类对象进行初始化时,可以使用explicit关键字修饰构造函数

#include <iostream>
using namespace std;class test
{
private:int _num;int _num1;
public:explicit test(int num, int num1):_num(num),_num1(num1){}void print(){cout << _num << " " << _num1 << endl;}
};int main()
{test t(2, 3);test t1 = { 2,4 };t.print();t1.print();return 0;
}
报错信息:
"test" 的复制列表初始化不能使用显式构造函数

但是,如果为已经实例化的对象再次赋值时,则会调用赋值运算符重载函数为对象赋值,如下面代码

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){}void print(){cout << _num << endl;}test(const test& d){_num = d._num;}test& operator=(const test& t){if (this != &t){_num = t._num;}return *this;}
};int main()
{test t(1);//为对象初始化t = 2;return 0;
}

在上面的代码中,t对象再次赋值时会调用赋值重载,首先会调用拷贝构造函数,但是会被编译器优化为直接调用构造函数,再调用赋值重载函数

static成员

static成员的介绍

当需要统计一个类创建了多少个对象时,第一反应是创建一个全局变量,当每一次调用构造函数或者拷贝构造函数时,就让其进行+1操作,但是这个思路的问题是该全局变量不仅是在类中可以访问,也可以在类外访问,此时如果在类外改变了该变量的值,那么此时的计数不一定准确,如果直接放置到类内作为类的成员变量,那么每一个类对象在创建时都会为这个成员变量分配独立的空间,那么每一个类对象的计数器变量都只为1,并没有达到计数的效果

为了解决上面的问题,C++支持在类中创建静态成员变量,该成员变量不属于任何一个实例对象,而属于整个类,但是因为在类内,所以类中的成员函数也可以直接访问该静态成员变量。一样的思路,当每一次调用构造函数或者拷贝构造函数时让该变量+1即可实现统计一个类有多少个对象,并且这个方法的好处是保证了类的封装性,如果该成员变量具有private属性,那么在类外也不可以直接修改(要修改时需要类中提供静态set成员函数),但是此时要在类外直接访问该静态成员变量时需要提供get函数,如下面的代码

#include <iostream>
using namespace std;class test
{
private://静态成员变量static int _count;//静态成员变量声明
public:test(){_count++;}test(const test& t){_count++;}//使用静态成员函数在类外访问静态成员变量static int getCount(){return _count;}
};int test::_count = 0;//静态成员变量初始化int main()
{test t;test t1;test t3(t1);//统计创建的对象的个数cout << test::getCount() << endl;return 0;
}
输出结果:
3

上面代码的作用是统计创建类对象的个数,基本思路是通过统计调用构造函数或者拷贝构造函数的次数来统计类对象的个数,类中存在一个静态成员变量_count,该成员变量从属于整个类而不是某一个单独的类对象,因为是静态成员变量,所以需要使用静态成员函数才可以在类域外访问(因为具有private属性,所以不可以直接通过类名进行调用)

static成员的使用

在C++中,声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量

同样,用static修饰的成员函数,称之为静态成员函数。

📌

注意:静态成员变量一定要在类外进行初始化

static成员的特点

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区,在使用sizeof计算类大小时同样不会包括static成员的大小
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问(前提是非private属性)
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受publicprotectedprivate 访问限定符的限制
#include <iostream>
using namespace std;class test
{
private:int _num;//非静态成员变量static int _count;//静态成员变量
public:test(int num):_num(num){}static int getCount(){_num = 1;//无法访问非静态成员变量return _count;}
};int main()
{test t(1);return 0;
}
报错信息:
非静态成员引用必须与特定对象相对

友元

在C++中,友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

友元分为:友元函数和友元类

友元函数

在默认构造函数章节中,对流插入运算符和流提取运算符进行重载时,如果将这两个运算符重载函数放置在类中时,那么ostreamistream的对象将与this指针抢占第一个参数的位置,导致最后调用时和正常的输出输入参数相反,而解决这个问题时考虑到将这两个重载函数放置到全局,但是放置到全局时将无法看到类成员变量,此时为了同时解决前面两个问题,考虑使用友元函数,如下面代码

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test():_num(){}//友元函数声明//友元函数声明可以放在任意位置friend ostream& operator<<(ostream& cout, const test& d);friend istream& operator>>(istream& cin, test& t);
};//重载流插入运算符
ostream& operator<<(ostream& cout, const test& t)
{cout << t._num;return cout;
}//重载流提取运算符
istream& operator>>(istream& cin, test& t)
{cin >> t._num;return cin;
}int main()
{test t;cin >> t;cout << t;return 0;
}
输入:
2
输出结果:
2

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

友元函数的特点:

  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同

友元类

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

友元类的特点:

  1. 友元关系是单向的,不具有交换性。
  2. 友元关系不能传递

💡

若C是B的友元类(即C想使用B类中的成员),B是A的友元类(即B想使用A类中的成员),不能推出C是A的友元类(即C依旧无法使用A中的成员)

  1. 友元关系不能继承(见继承章节)
#include <iostream>
using namespace std;class B
{
private:int _numB;friend class A;//声明A是B的友元类
public:B():_numB(2){}
};class A
{
private:int _numA;B _b;
public:A():_numA(){}void print(){cout << _b._numB << endl;}
};int main()
{A a;a.print();return 0;
}
输出结果:
2

在上面的代码中,定义了两个类A和B,在类B中声明了类A为B的友元类,则A类中可以访问B中的成员

📌

注意,尽管A类是B类的友元类,但是B类不可以访问A类中的成员,即单向传递

💡

友元类的声明规则:当某个类想使用另一个类中的成员时,就在另一个类中声明友元类,例如在上面的代码中,A类想使用B类的成员,则A类在B类声明友元类(即想用哪一个类就在哪个类声明友元类)

内部类

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。

📌

内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元,即外部类不可以访问内部类成员

内部类特点:

  1. 内部类可以定义在外部类的publicprotectedprivate都是可以的(若内部类具有private属性,则不能使用外部类::内部类 对象名 的方式创建对象)
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
  3. sizeof(外部类)=外部类,和内部类没有任何关系
  4. 内部类虽然与外部类独立,但是内部类的生命周期还是外部类域
#include <iostream>
using namespace std;class outer
{
private:int _num;static int _num1;public:outer():_num(){}class inner{private:int _num2;public:void print(const outer& o){cout << o._num << endl;//非静态成员需要外部类对象cout << _num1 << endl;//静态成员可以不使用外部类对象}};
};int outer::_num1 = 2;int main()
{outer o;outer::inner i;i.print(o);return 0;
}
输出结果:
0
2

在上面的代码中,外部类outer中有一个inner内部类,该内部类中可以通过外部类的对象o访问外部类的成员,而对于外部类的静态成员则可以直接访问

📌

注意,内部类中不可以创建外部类对象

#include <iostream>
using namespace std;class outer
{
private:int _num;static int _num1;
public:outer():_num(){}class inner{private:int _num2;public:void print(const outer& o){cout << o._num << endl;//非静态成员需要外部类对象cout << _num1 << endl;//静态成员可以不使用外部类对象}};
};int outer::_num1 = 2;int main()
{outer o;outer::inner i;i.print(o);return 0;
}
报错信息:
“outer::inner::_o”使用未定义的 class“outer”

因为内部类和外部类是两个单独的类,所以在使用sizeof计算外部类大小时不会包括内部类的大小

#include <iostream>
using namespace std;class outer
{
private:int _num;static int _num1;
public:outer():_num(){}class inner{private:int _num2;};
};int outer::_num1 = 2;int main()
{cout << sizeof(outer) << endl;return 0;
}
输出结果:
4

匿名对象

所谓匿名对象,即类对象在实例化时不给名称

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){}void print(){cout << _num << endl;}
};int main()
{//普通对象test t(1);t.print();//匿名对象test(2).print();return 0;
}
输出结果:
1
2

在上面的代码中,创建了两个对象,一个对象是普通对象,普通对象的生命周期为当前函数栈帧空间销毁之前,另外一个对象是匿名对象,匿名对象的生命周期为所在行,当匿名对象执行完所在行之后就会被销毁

📌

注意:匿名对象与常量一样,当给自定义类型的引用变量时需要有const

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

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

相关文章

Stable diffusion(四)

训练自己的Lora 【DataSet】【Lora trainer】【SD Lora trainer】 前置的知识 batch size&#xff1a;模型一次性处理几张图片。一次性多处理图片&#xff0c;模型能够综合捕捉多张图片的特征&#xff0c;最终的成品效果可能会好。但是处理多个batch size也意味着更大的显存…

nvm更换node.js的版本

自行下载nvm 打开cmd 1. nvm ls 列出目前已经下载的node版本&#xff0c;和正在使用的node版本 2. nvm install v版本号 下载某个版本 3. nvm uninstall v版本号 卸载某个版本 4. nvm use 版本号 切换到某个版本

深入理解栈和队列(二):队列

个人主页&#xff1a;17_Kevin-CSDN博客 专栏&#xff1a;《数据结构》 一、队列的概念和结构 队列是只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的…

吴恩达2022机器学习专项课程(一) 3.5 可视化成本函数

问题预览 为什么要可视化成本函数&#xff1f;可视化之后的成本函数是什么样子&#xff1f;如何在三维空间里通过w和b找到一个成本函数的值&#xff1f;如何在三维空间里找到成本函数的最小值&#xff1f; 解读 可视化成本函数&#xff1a;为了更加方便的看到不同的w和b&…

AI:152- 利用深度学习进行手势识别与控制

本文收录于专栏:精通AI实战千例专栏合集 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 每一个案例都附带关键代码,详细讲解供大家学习,希望可以帮到大家。正在不断更新中~ 一. 利用深度学习进行手势识别与控制 …

Acrobat Pro DC ----专业PDF编辑与管理

Acrobat Pro DC 2023是一款功能强大的PDF处理软件&#xff0c;它提供了丰富的编辑工具&#xff0c;支持创建、编辑、合并、分割PDF文件&#xff0c;以及高质量的PDF到其他格式的转换功能。同时&#xff0c;该软件集成了最新的OCR技术&#xff0c;可将扫描文档或图片转换成可编辑…

转座子插入位点分析4------PS转座子测序数据分析

观察数据 这是经公司使用fastp质控后的数据&#xff0c;我们先挑选部分数据进行比对&#xff0c;观察序列结构 为了准确性&#xff0c;我们再次挑选另一批数据进行比对 可以看到&#xff0c;所有序列都存在一个“GTGTCAAATACTTATTTTCCCCGCTGTA”的前导序列&#xff0c;这可能…

《大厂面试模拟(免费) - C++工程方向》

以个人的名义&#xff0c;提供c工程方向的大厂面试模拟&#xff0c;不会以任何形式收费。此面试可为应聘者提供真实反馈。简历和面试过程不会以任何形式给第三方&#xff08;包括我当前所在公司&#xff09;。 我的经验&#xff1a; 多年百度&#xff0c;阿里知名核心项目组工…

Linux快速安装FFmpeg、ffprobe、ffplay以及在Linux上的使用

文章目录 一、工具简介二、CentOS7上安装FFmpeg2.1 方法一&#xff1a;yum安装2.2 方法二&#xff1a;源码安装 三、ffprobe 在linux中的用法3.1 显示多媒体文件的基本信息3.2 显示特定流的信息3.3 获取音视频的时长 一、工具简介 这些工具都是与多媒体处理和流媒体相关的开源…

uniapp页面嵌套其他页面的实现

功能: 类似于一个drawer&#xff0c;当主页面加载的时候会一并加载url对应的组件&#xff0c;当点击后以drawer形式显示组件里面的内容&#xff0c;可动画。 <navigator url"/pages/my/components/personalMessage" slot"right"><view><di…

Java螺旋折线

题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 (X,Y)&#xff0c;我们定义它到原点的距离dis(X,Y) 是从原点到 (X,Y) 的螺旋折线段的长度。 例如 dis(0,1)3&#xff0c;dis(−2,−1)9。 给出整点坐标 (X,Y)&#xff0c;你能计算出 dis(X,Y) 吗&#xf…

GO-初识包管理

初识包管理&#xff0c;知道项目中文件和文件夹之间的关系 输出&#xff0c;代码&#xff0c;在go编译器运行时会显示在屏幕中 初识数据类型 整型&#xff0c;数字。例如&#xff1a;1、2、3、4 字符串类型&#xff0c;表示文本信息的。例如:“张三”“李四” 布尔类型&#x…

图解Kafka架构学习笔记(三)

准备Kafka环境 这里推荐使用Docker Compose快速搭建一套本地开发环境。 以下docker-compose.yml文件用来搭建一套单节点zookeeper和单节点kafka环境&#xff0c;并且在8080端口提供kafka-ui管理界面。 version: 2.1services:zoo1:image: confluentinc/cp-zookeeper:7.3.2hos…

FPGA 以太网传输ov5640视频

1 实验任务 使用 DFZU4EV MPSoC 开发板及双目 OV5640 摄像头其中一个摄像头实现图像采集&#xff0c;并通过开发板上的以太网接口发送给上位机实时显示。 2 系统框架 时钟模块用于为 I2C 驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟&#xff1b;I2C 驱动模块…

Python Flask 自定义错误页面

新建templates/404.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <img src"../static/a123.png" alt"" width"…

基于spring boot的个人博客系统的设计与实现(带源码)

随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的个人博客系统&#xff0c;有管理员&#xff0c;用户&#xff0c;博主…

使用Linux别名简化命令输入

Linux定义命令别名&#xff0c;解决经常重复输入长命令 在Linux环境下工作时&#xff0c;我们经常需要输入长长的命令&#xff0c;这不仅耗时而且容易出错。Linux提供了一种名为“别名&#xff08;alias&#xff09;”的功能&#xff0c;可以让我们为这些长命令定义简短的名称…

使能 Linux 内核自带的 FlexCAN 驱动

一. 简介 前面一篇文章学习了 ALPHA开发板修改CAN的设备树节点信息&#xff0c;并加载测试过设备树文件&#xff0c;文件如下&#xff1a; ALPHA开发板修改CAN的设备树节点信息-CSDN博客 本文是学习使能 IMX6ULL的 CAN驱动&#xff0c;也就是通过内核配置来实现。 二. 使能…

【数据结构】归并排序(用递归)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解归并排序&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一. 基本思想二. 归并排序代码 一. 基本思想 在讲归并排序之前&#xff0c;问问自己&#x…

使用Docker搭建Syslog-ng

Syslog-ng是一个可靠、多功能的日志管理系统&#xff0c;用于收集日志并将其转发到指定的日志分析工具。 使用Docker CLI方式搭建 步骤 1: 拉取Syslog-ng镜像 首先&#xff0c;需要从Docker Hub拉取Syslog-ng的官方镜像。 docker pull balabit/syslog-ng:latest步骤 2: 启动…