类和对象 02【C++】

文章目录

  • 一、 构造函数(初始化列表)
    • 1. 初始化列表
    • 2. explicit 关键字
    • 3. static成员
  • 二、 友元
    • 1. 友元函数
    • 2.友元类
  • 三、 内部函数
  • 四、 匿名对象
  • 五、 拷贝对象时的一些编译器优化

一、 构造函数(初始化列表)

进一步理解构造函数,我们知道创建对象时,编译器通过构造函数,初始化对象,给对象中的成员变量一个合适的初始值

平时我们写的构造函数,以日期类为例:

class Date 
{
public:Date(int year,int month,int day) {_year = year;_month = month;_day = day;}
private:
//成员声明int _year;int _month;int _day;
};

上述构造函数调用后,对象有了初始值,但是不能将其称为对 对象中的成员变量的初始化,而是在构造函数体进行赋值的
构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以进行多次赋值

1. 初始化列表

初始化列表: 以一个冒号开始,接着就是用逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或者是表达式

	//初始化列表Date(int year, int month, int day):_year(year),_month(month),_day(day){}

上述用于完成日期类的初始化

对于普通构造函数可能会:
在这里插入图片描述
当对象实例化后,对象要进行整体定义,每个成员要去调用构造函数,每个成员需要有定义初始化的地方,在C++中,构造函数的初始化列表,就是每个成员定义初始化的地方

在这里插入图片描述

成员变量定义初始化,上图_year _month _day 也会走初始化列表,只是没有给值,所以是随机值

在这里插入图片描述
上述给值,是缺省值,这个值是给初始化列表用的

  • 当初始化列表没有显示初始化的时候,回去调用缺省值(有缺省值的时候就初始化缺省值,否则就给随机值)
  • 当初始化列表初始化了,就直接初始化(不会去用缺省值了,因为初始化列表,已经初始化了)

所以当没有使用初始化列表,才会使用缺省值

在这里插入图片描述
先走初始化列表,再走函数体

能用初始化列表就用初始化列表

	Date(int year,int month,int day):_year(year),_month(month),_day(day),_n(1){}

我们知道一些成员定义的时候必须走初始化列表

const int _n;	//const修饰的变量
int&  _ref;			//引用必须在定义的时候初始化
class Date 
{
public:Date(int year,int month,int day,int& x):_year(year),_month(month),_day(day),_n(1),_ref(x){}
private:int _year;int _month;int _day;const int _n;	//const修饰的变量int& _ref;			//引用必须在定义的时候初始化
};

在这里插入图片描述

当我们不写,编译器自动生成的默认构造函数,对于内置类型不做处理,对于自定义类型去调用它的默认构造

自定义类型的成员变量必须放在初始化列表位置进行初始化(没有默认构造函数时)
在这里插入图片描述

下方代码,初始化列表中没有写对自定义成员的初始化,这里的自定义成员回去调用它自己的默认构造

class A 
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};
class Date 
{
public:Date(int year,int month,int day,int& x):_year(year),_month(month),_day(day),_n(1),_ref(x){}
private:int _year;int _month;int _day;const int _n;	//const修饰的变量int& _ref;		//引用必须在定义的时候初始化A _aa;			//自定义类型的成员变量
};

当自定义成员没有默认构造,初始化列表中也没有定义初始化时
会报错(没有合适的默认构造函数可以用)
在这里插入图片描述
当自定义类型 初始化列表和默认构造都写的情况下,会走初始化列表

初始化列表注意点

  • 每个成员变量在初始化列表中只能初始化一次

  • 类中的以下成员必须在初始列表位置进行初始化

    • 引用成员变量
    • const成员变量
    • 自定义类型成员变量(没有写默认构造函数的情况下)
      上述三个不能在函数体内初始化
  • 其他成员变量可以在初始化列表,也可以在函数体内,建议在初始化列表,当然也可以混着使用

class Date 
{
public:Date(int year,int month,int day,int& x):_year(year),_month(month),_day(day),_p((int*)malloc(sizeof(4))){if (_p == nullptr){perror("malloc fail");exit(-1);}}
private:int _year;int _month;int _day;int* _p;
};
  • 成员变量在类中声明的顺序就是其在初始化列表中的初始化顺序,与初始化列表中的先后次序无关

分析下方代码:

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}//
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值

答案是: 选项D
因为成员变量在类中声明的顺序,就是初始化的顺序,在初始会去走初始化列表
先走_a2,但是此时_a1还是随机值,所以_a2为随机值,之后再走_a1,_a1的值初始化为1
打印结果 先打印_a1,再打印_a2 的结果
在这里插入图片描述

2. explicit 关键字

在C++中,explicit关键字主要用于类的构造函数声明中,它的主要目的是阻止隐式类型转换的发生。当一个类的构造函数被声明为explicit时,编译器将不会使用这个构造函数进行任何自动类型转换。

先看一下类型转化

//类型转换int i = 1;//double& b = i;	//error,类型转换产生临时变量,临时变量具有常性const double &b = i;	//所以这里要加上一个const//对于自定义类型// A& aa3 = 2;	//error,原因是 (单参数构造函数支持隐式类型转换,类型转换产生临时变量)aa3引用的不是2,而是临时变量,临时变量具有常性const A& aa3 = 3; //内置类型赋值给自定义类型,发生隐式类型转换

对于单参数构造函数支持隐式类型转换

class A 
{
public://构造A(int x = 0):_x(x){}构造//explicit A(int x = 0)	//如果不想让隐式类型转换发生,使用explicit//	:_x(x)//{}//拷贝构造A(const A &aa){cout << "	A(const A &aa)" << endl;}private:int _x;
};
int main() 
{A aa1(6);	//构造//单参数构造函数支持隐式类型转换//这里5先去调用构造A的一个临时对象,然后再去拷贝构造A aa2 = 5;//不同类型之间进行赋值,中间会产生临时对象 (所以会构造+拷贝构造)//但是 有些编译器会优化 同一个表达式连续步骤的构造,一般会被合二为一,把拷贝构造给合并了return 0;
}  

在C++98中是不支持多参数隐式类型转换的,C++11是支持多参数隐式类型转换的

	D dd1 = {6,6};

上述的隐式类型转换,如果不想让其发生则需要,使用explicit 关键字

	explicit A(int x = 0)	//如果不想让隐式类型转换发生,使用explicit:_x(x){}

给成员变量缺省值的一些情况

	//给缺省值int _a = 1;int* p = (int*)malloc(4);D dd1 = {1,2};

3. static成员

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

例如:
实现一个类,计算程序中创建了多少个类对象

//static 成员
//实现一个类,计算程序中创建了多少个类对象int  n = 0;	//用一个全局变量去计算构造函数走了多少次
class A 
{
public:A() {++n;}A(const A& aa) {++n;}
};
A func() 
{A aa;return aa;
}
int main() 
{A aa1;A aa2;func();cout << n << endl;//对于创建多少个类对象,我们可以去统计构造函数构造了多少次return 0;
}

对于上方代码,全局变量n是很容易让修改的,所以把 n封装,使其不被修改

// 上述的n容易被修改
// 所以把 n封装,使其不被修改
class A
{
public:A(){ ++n; }A(const A& aa){++n;}//静态成员函数//静态成员函数和普通的成员函数的区别是,静态成员函数没有this指针//静态成员变量的特点是:比如定义一个全局的又想用类进行封装,那就可以定义静态成员变量//访问静态成员变量,提供对应的静态成员函数,来访问静态成员变量static int GetN() {return n;}
private://int n = 0; //这样是不能加上同一个n的//声明static int n;	//注意这里的n不是属于某一个对象,而是属于所有对象,属于整个类,要在类外面进行定义
};//定义,静态成员变量一定要在类外进行初始化
int A::n = 0;A func()
{A aa;return aa;
}
int main()
{A aa1;A aa2;func();//对于私有的情况下,获取n要提供一个类中的公有成员函数gettercout <<aa1.GetN()<< endl;cout << A::GetN()<< endl;	//静态成员函数允许类域访问//对于创建多少个类对象,我们可以去统计构造函数构造了多少次return 0;
}

注意:定义和初始化静态成员变量一定要在类外进行
在这里插入图片描述
所以,静态成员变量可以在类里面声明,但是定义和初始化是在类外面进行的在C++中,静态成员变量在类外部定义和初始化是为了确保它们在整个程序范围内唯一存在、正确初始化以及避免多重定义的问题

static成员小总结:

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

二、 友元

“友元”(Friend)是一个特殊的访问权限控制机制。
友元提供了一种突破封装的方式,为有些情况提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

友元 分为 友元函数友元类

1. 友元函数

在重载操作符流插入 << 和流提取 >> 时,一般写成全局的函数才能完成。函数去访问成员变量时,受到封装的限制,要获取封装的成员变量,使用getter和setter来完成。

但是使用友元函数去封装类中声明,就可以获取到访问私有(private)和保护(protected)成员的权限
例如 重载操作符流插入 << 和流提取 >> 使用 友元去封装的类进行声明,声明时需要加friend关键字

friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in,Date& d);

那为什么 重载操作符流插入 << 和流提取 >> 要写成全局的函数呢?

因为如果写成 成员函数cout的输出流对象和隐含的this指针会抢占第一个参数的位置。this指针默认是第一个参数(左操作数),实际情况是cout的输出流对象需要是第一个参数位置才可以,所以要operator<<重载成全局函数。operator>>同理。

友元函数小总结:

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

2.友元类

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

//友元类
class Time
{//声明 日期类 为 时间类 的友元类,则日期类就可以直接访问时间类的私有成员变量friend class Date;
public:Time(int hour = 0,int minute = 0,int second = 0) :_hour(hour),_minute(minute),_second(second){}
private:int _hour;int _minute;int _second;
};class Date 
{
public:Date(int year = 1970,int month = 1,int day = 1):_year(year),_month(month),_day(day){}void set_tmie_date(int hour,int minute,int second){//可以直接访问时间类的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};

友元类 小总结:

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

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

  • 友元关系不能继承

三、 内部函数

一个类定义在另一个类的内部,这类叫做内部类
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。
内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员(外部类不是内部类的友元)

class A 
{
public:class B {private:int _b1;};
private:int _a1;int _a2;
};

小总结:

  • 内部类可以定义在外部类的public、protected、private都是可以的
  • 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象。
  • sizeof(外部类)=外部类,和内部类没有任何关系

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

(1) 方法1不使用内部类

class Sum
{
public:Sum(){_ret += _i;_i++;}static int get() //通过静态成员函数来返回私有静态成员变量{return _ret;}
private://静态成员变量,类内部声明static int _ret;static int _i; 
};
//静态成员变量,类外部定义和初始化
int Sum::_ret = 0;
int Sum::_i = 1;class Solution {
public:int Sum_Solution(int n) {Sum arr[n]; //数组构造n个Sum类型的对象return Sum::get();}
};

(2) 使用内部类

class Solution {class Sum{public:Sum() {_ret += _i;_i++;}};public:int Sum_Solution(int n) {Sum arr[n];return _ret;}
private:static int _ret;static int _i;
};
int Solution::_ret = 0;
int Solution::_i = 1;

四、 匿名对象

在C++中,匿名对象是指没有显式名称的对象,通常用于临时存储数据或执行某些操作。这些对象在创建后会立即被初始化,并且通常只在当前的代码块或表达式中有效。

首先 一般情况下都是定义有名对象
例如:
在这里插入图片描述
那么 匿名对象,就没有上述的a1名字

	A(2);	//匿名对象
	A a1(1);//有名对象A(2);	//匿名对象,好处就是不用取名字,生命周期只有一行,运行完这一行就会自动调用析构函数//有名对象Solution s1;s1.Sum_Solution(10);//匿名对象Solution().Sum_Solution(1);

匿名对象,好处就是不用取名字,生命周期只有一行,运行完这一行就会自动调用析构函数

五、 拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

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

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

相关文章

手把手教会你 - StreamAPI基本用法

1. 简介 目前响应式编程的学习中很多时候都用到了Lambda表达式和StreamAPI&#xff0c;那么今天就在这里记录一下一些最基本的使用方法。 StreamAPI中引入了流的概念&#xff0c;其将集合看作一种流&#xff0c;流在管道中传输&#xff08;动态的&#xff09;&#xff0c;可以…

IDEA构建Maven JavaSE工程的全面指南

IDEA构建Maven JavaSE工程的全面指南 一、引言 在现代Java开发中&#xff0c;Maven已经成为了一个不可或缺的工具&#xff0c;它帮助我们管理项目的依赖、构建、文档、报告等。而IntelliJ IDEA&#xff08;简称IDEA&#xff09;则是一款强大的Java集成开发环境&#xff08;ID…

AI助力剧本创作:如何5分钟内构思出热门短剧大纲

人工智能重塑短剧行业&#xff1a;从剧本创作到市场推广 在当今短剧行业的飞速发展中&#xff0c;剧本创作的质量及其更新的速度已然成为短剧能否转化为热门作品的关键性因素。然而&#xff0c;随着短剧创作成本的日益攀升&#xff0c;一个卓越的剧本无论在创作时间上还是在构思…

【JAVA重要知识 | 第六篇】Java集合类使用总结(List、Set、Map接口及常见实现类)以及常见面试题

文章目录 6.Java集合类使用总结6.1概览6.1.1集合接口类特性6.1.2List接口和Set接口的区别6.1.3简要介绍&#xff08;1&#xff09;List接口&#xff08;2&#xff09;Set接口&#xff08;3&#xff09;Map接口 6.2Collection接口6.3List接口6.3.1ArrayList6.3.2LinkedList—不常…

2023年第三届中国高校大数据挑战赛(第二场)B题思路

竞赛时间 &#xff08;1&#xff09;报名时间&#xff1a;即日起至2024年3月8日 &#xff08;2&#xff09;比赛时间&#xff1a;2024年3月9日8:00至2024年3月12日20:00 &#xff08;3&#xff09;成绩公布&#xff1a;2024年4月30日前 赛题方向&#xff1a;文本或图象分析方…

5.51 BCC工具之slabratetop.py解读

一,工具简介 slabratetop工具以类似于 top 命令的实时刷新显示方式,展示从内核内存分配缓存(SLAB 或 SLUB)中的分配速率和总字节数。例如: (注:SLAB 和 SLUB 是 Linux 内核中用于内存管理的两种不同机制,它们都提供了一种方式来缓存和管理小对象的内存分配。) 二,…

学生信息管理APP

设计内容简介 本次设计使用Android Studio实现一个学生信息管理系统,系统功能结构如下图所示: 详细设计 数据库设计SQLite,是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低。…

143:vue+leaflet 在25833投影坐标下,加载一小块图像叠层数据

第143个 点击查看专栏目录 本示例是介绍如何在vue+leaflet, 自定义CRS,形成新的投影,这里是25833投影,并使用 L.Proj.imageOverlay的方法在地图上加载载一小块图像叠层数据。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式…

Python爬虫——Scrapy-1

目录 简介 安装 基本使用 1. 创建爬虫的项目 2. 创建爬虫文件 3. 运行爬虫代码 scrapy项目组成 scrapy工作原理 ​编辑 58同城 scrapy架构组成 汽车之家 总结 简介 Scrapy 是一个基于 Python 的开源网络爬虫框架&#xff0c;它可以帮助开发者快速、高效地构…

js 实现点击按钮小球加入购物车动画

本文旨在实现类似点击按钮实现小球加入购物车效果。 使用技术&#xff1a; Vue2使用 Pubsub 监听按钮点击事件&#xff08;如果不想用也可以自己改造下&#xff09;监听 onmousemove 来获取按钮点击时的鼠标位置 小球组件&#xff1a; html css&#xff1a; 小球父元素&am…

【新国标送检】

新国标送检 ■ 设备型号差异性检测■■■■ ■ 设备型号差异性检测 TMS-21&#xff0c;TMS-23&#xff0c; TMS-25 各个型号都送一台过去做差异性检测。 ■ ■ ■ ■

机器学习 | 使用CatBoost处理缺失值

数据是任何分析或机器学习的基础。然而&#xff0c;现实世界的数据集并不完美&#xff0c;它们经常包含缺失值&#xff0c;这可能导致任何算法的训练阶段出现错误。处理缺失值至关重要&#xff0c;因为它们可能会导致数据分析和机器学习模型中出现偏差或不准确的结果。处理缺失…

C语言 goto 语句的基本格式是什么?如何使⽤?

一、问题 goto 语句为⽆条件转向语句&#xff0c;它可以使程序⽴即跳转到函数内部的任意⼀条可执⾏语句&#xff0c;这样使⽤起来⽐较灵活。那么&#xff0c;该语句的基本格式是什么&#xff1f;又该如何使⽤呢&#xff1f; 二、解答 1. goto 语句的基本格式 goto 关键字后⾯…

每日五道java面试题之springMVC篇(一)

目录&#xff1a; 第一题. 什么是Spring MVC&#xff1f;简单介绍下你对Spring MVC的理解&#xff1f;第二题. Spring MVC的优点第三题. Spring MVC的主要组件&#xff1f;第四题. 什么是DispatcherServlet?第五题. 什么是Spring MVC框架的控制器&#xff1f; 第一题. 什么是S…

子查询与连表查询

子查询与连表查询 标签:数据库 子查询 mysql> explain select e.empno,e.ename,(select dname from dept d where e.deptno d.deptno) as dname from emp e where e.deptno 1; -------------------------------------------------------------------------------------…

中间件 | Redis - [big-key hot-key]

INDEX 1 big-keyhot-key 1 big-key 分类 字符串型 big-key&#xff1a;字符串最大可以到 512M集合型 big-key&#xff1a;集合个数可以到 2^23 问题 内存空间不均匀指令耗时增加&#xff1a;redis 是单线程的&#xff0c;部分操作的时间复杂度是 O(n) 的&#xff0c;big-ke…

Java后端 - 一面凉经 - 得物(国际电商)

招聘流程&#xff1a; 2024.1.5 HR邮件沟通 2024.1.7 上午面试 面试流程&#xff1a; 简单自我介绍 简单问实习工作 介绍目前Java项目 如何在项目中处理并发问题 讲讲并发编程 如何使用锁 问什么like查询慢 explain具体讲讲 加索引为什么会变快 索引是什么数据结构…

python-批量操作excel

批量新增excel文件 import osimport xlwings as xwapp xw.App(visibleTrue,add_bookFalse)#visible设置为ture的时候会自动打开创建的excel文件&#xff0c;设为为false的时候不会看到excel文件打开了&#xff0c;实际进程占用了....dept_list [人事部,财务部,研发部,行政部…

Manz高压清洗机S11-028GCH-High Quality Cleaner 操作使用说明492页

Manz高压清洗机S11-028GCH-High Quality Cleaner 操作使用说明492页

Kafka 设计之消息传递保障

目录 一. 前言 二. Kafka 消息传递保障设计 一. 前言 消息传递保障对于分布式系统的可靠性至关重要。在分布式系统中消息传递保障是确保系统可靠性的核心问题之一。系统需要确保消息能够按照预期的方式进行传递&#xff0c;以满足业务需求。 Kafka 是一种分布式的消息队列系统…