C++:类和对象(下)

目录

1. 再谈构造函数

1.1构造函数题赋值

1.2初始化列表

初始化列表有什么用呢?

1.3 explicit关键字

2. Static成员

2.1概念

2.2特性

3. 友元

3.1友元函数

3.2友元类

4. 内部类(了解)

5.匿名对象

6.拷贝对象时的一些编译器优化


学习目标

  • 1. 再谈构造函数
  • 2. Static成员
  • 3. 友元
  • 4. 内部类
  • 5.匿名对象
  • 6.拷贝对象时的一些编译器优化

1. 再谈构造函数

1.1构造函数题赋值

构造函是编译器调用其时,给对象中各成员变量一个合适的初始值

class Date
{
public:
Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
private:
int _year;
int _month;
int _day;
};

调用该函数后,对象中的成员变量会得到一个初始值,但是不能叫初始化

原因:初始化只能初始化一次,构造体函数内可以多次赋值(上述操作只能被成为赋值)

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. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

  • 引用的成员变量
  • const成员变量
  • 自定义类型成员(且该类没有构造函数)


原因:

--引用的成员变量:引用必须在定义的时候初始化

--const成员变量:const必须在定义的时候初始化(只有一次机会)

--自定义类型成员(没有默认构造函数)必须初始化:和下面类似,若Date类里面有一个自定义类型的成员变量 Time _t ,若其没有适合的构造函数,会使得Date的实例化不了,所以其必须初始化

3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

示例:

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,先初始化_a2,_a2是用_a1初始化(_a1此时是随机值),

                                           然后初始化_a1, _a1用1初始化

所以最后,_a1 = 1, _a2  = 随机值;

初始化列表有什么用呢?

原来的构造函数是函数体内初始化,总会面临一些不好处理的情况(如下),因此引入初始化列表解决问题

--可以用来处理默认构造函数不适配的情况,当一个类(Time)给了一个不适配的构造函数,我们可以用初始化列表处理这个类

问题引入:现给一个Time类,提供一个带参数的构造函数,Date类里面包含Time类,我们该如何初始化Time类?

1.正常情况:Time类没有默认构造函数,编译器要处理自定义类型,会生成一个默认构造函数,但编译器又不会处理内置类型

示例:

class Time 
{
public://不给构造函数
private:int _hour;
};class Date
{
public:private:int _year;Time _t;
};int main() 
{Date d1;//Date和Time类都不给默认构造函数return 0;
}

这里看起来编译器什么都没做:

实际上--用Date去实例化d1,它的成员变量_year(不处理),

_t(自定义类型Time)--去调用它的默认构造函数,但Time没给默认构造函数,所以编译器自动生成一个默认构造函数,但其不会处理内置类型,Time的成员变量又是内置类型

所以看起来编译器什么都没做

2.Time类给一个不适配的构造函数:

class Time 
{
public:Time(int hour) //给个不适配的构造函数{_hour = hour;}
private:int _hour;
};class Date
{
public:private:int _year;//Time _t;
};int main() 
{Date d1;return 0;
}

这里就会发现,我们处理不了Time类,并且实例化不了Date类

然后我们使用初始化列表解决:

class Time 
{
public:Time(int hour) //不适配的构造函数{_hour = hour;}
private:int _hour;
};class Date
{
public://要初始化_t只能通过初始化列表Date(int year, int hour):_t(hour)//初始化列表解决自定义类型成员变量没有构造函数的情况{_year = year;}private:int _year;Time _t;
};int main() 
{Date d(2023,1);//给Date构造函数传值return 0;
}

这里就解决了,自定义类型的成员变量没有构造函数的情况

总结

1.自定义类型成员,推荐使用初始化列表初始化

2.初始化列表可以认为是成员变量定义的地方

1.3 explicit关键字

功能:explicit是阻止隐式类型转换的

隐式类型转换:

int i = 10;
double d = i;
//会产生临时变量tmp,把i转换为double类型后再拷贝给d

验证:

int i = 10;
double& d = i;
//这里引用是引用的临时变量,临时变量具有常性,不能引用

这里加个const也行,因为现在是权限的平移(const 修饰的变量不能修改,常量也不能修改,加上const后再引用也不能修改)

有什么用呢?示例:

现在加上const:

这里做到的优化:可以不用构造一个string对象就能传参

也说明了:传参尽量用引用,用引用尽量加上const

传引用相比于传值调用:不用创建临时变量去拷贝,会更快一些

接下来看下面这段代码:

class Date
{
public://构造函数Date(int year):_year(year){cout << "Date(int year)" << endl;}//拷贝构造Date(const Date& d) {cout << "Date(const Date& d)" << endl;}private:int _year;
};int main() 
{Date d1(2023);//直接调用构造Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)return 0;
}

   1-- Date d1(2023);//直接调用构造
   2--Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)

   2中会先创建一个Date类型的临时变量tmp,然后把2023转换为Date类型拷贝构造给d2

现在在构造函数上为其加上  explicit关键字,就能阻止隐式类型的转换

class Date
{
public://构造函数explicit Date(int year)//加上explicit:_year(year){cout << "Date(int year)" << endl;}//拷贝构造Date(const Date& d) {cout << "Date(const Date& d)" << endl;}private:int _year;
};int main() 
{Date d1(2023);//直接调用构造Date d2 = 2023;//构造 + 拷贝构造 + 优化  ==》 直接调用构造(隐式类型转换)return 0;
}

2. Static成员

2.1概念

声明为static的类成员称为类的静态成员

用static修饰的成员变量,称之为静态成员变量;

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

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

问题1:实现一个类,来计算程序创建了多少个类对象

class A 
{
public:A() { ++_count; }//构造函数A(const A& a) { ++_count; }//拷贝构造函数~A() { --_count; }//析构函数//private:static int _count;//声明int _a;
};//类外定义初始化
int A::_count = 0;

2.2特性

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

静态成员为所有类对象所共享:

A a1;
A a2;
A a3;

这里使用A类实例化了3个对象,_count是静态成员变量,这3个类对象共享

                                                ,_a是每个对象独立有的

若static修饰的成员变量是私有的,我们该怎么访问呢?

--可以使用static修饰的成员函数来获取:

class A 
{
public:A() { ++_count; }//构造函数A(const A& a) { ++_count; }//拷贝构造函数~A() { --_count; }//析构函数//静态成员函数    ---   没有this指针static int GetCount() {return _count;}private://静态成员变量,属于整个类,在静态区static int _count;//声明int _a;
};//类外定义初始化
int A::_count = 0;int main() 
{A a1;cout << A::GetCount() << endl;return 0;
}

问题:
1. 静态成员函数可以调用非静态成员函数吗?

--不能,静态成员函数没有隐藏的this指针,不能访问任何非静态成员
2. 非静态成员函数可以调用类的静态成员函数吗?

--可以,静态成员函数为所有类对象所共享

2.设计一个只能在栈上定义的对象的类

示例

class StackOnly 
{
public:static StackOnly CreateObj() {StackOnly s;return s;}private:StackOnly()//构造函数:_x(0),_y(0){}
private:int _x;int _y;
};int main() 
{//显示定义的构造函数不加private,创建的对象可以在栈,静态区//StackOnly s1;		//栈上//static StackOnly s2;//静态区上//显示定义构造函数并加上private,让其只能通过调用成员函数来创建对象//要调用这个函数我们加上static即可StackOnly s = StackOnly::CreateObj();return 0;
}

3. 友元

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

        分类:友元函数和友元类

3.1友元函数

功能:友元函数可以直接访问类的私有成员

语法:它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

示例:

class A 
{friend int sum(const A& a);
public:private:int _a;int _b;
};int sum(const A& a) 
{return a._a + a._b;
}

补充:

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

3.2友元类

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

  • 友元关系是单向的,不具有交换性。

如:Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

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

4. 内部类(了解)

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。


注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数

来访问外部类中的所有成员。但是外部类不是内部类的友元。

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

示列

class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());return 0;
}

5.匿名对象

语法:

Date(2023);//匿名对象

特点:生命周期只有这一行

作用:当我们只想调用类里面的函数的时候,就可以使用匿名对象,而不用实例化对象

//使用匿名对象调用类里的成员函数Date().PrintYear(2023);//实例化对象调用类里的成员函数Date d1(2023);d1.PrintYear(2023);

6.拷贝对象时的一些编译器优化

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

优化:连续一个表达式步骤中,连续的构造一般都会优化:例如构造+拷贝构造 --> 构造

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/64626.shtml

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

相关文章

2023-9-2 Kruskal算法求最小生成树

题目链接&#xff1a;Kruskal算法求最小生成树 #include <iostream> #include <algorithm>using namespace std;const int N 200010;// 与并查集中的p含义相同 int p[N];struct Edge {int a, b, w;bool operator< (const Edge & W)const{return w < W.w…

华为云云服务器评测 [Vue3 博物馆管理系统] 使用Vue3、Element-plus菜单组件构建轮播图

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus菜单组件构建轮播图 [第四章 使用Vue3、Element-plus菜单组件构建组图文章] 华为云云…

【【STM32--28--IO引脚的复用功能】】

STM32–28–IO引脚的复用功能 STM32的IO复用功能 何为复用? 我们先了解一下何为通用 IO端口的输入或输出是由GPIO外设控制&#xff0c;我们称之为通用 复用&#xff1a; IO端口的输入或者是输出是由其他非GPIO外设控制就像经常说的USART 由 DR寄存器进行输出 STM32的IO复用功…

GE MRP680489 IS200VTCCH1CBB印刷电路板

信号处理&#xff1a; 这个印刷电路板通常用于信号处理和数据传输&#xff0c;可以与其他设备或模块进行通信&#xff0c;如传感器、执行器或控制器。 通信接口&#xff1a; IS200VTCCH1CBB 可能具有多种通信接口&#xff0c;用于与其他设备和系统进行数据交换&#xff0c;包括…

软件外包开发人员分类

在软件开发中&#xff0c;通常会分为前端开发和后端开发&#xff0c;下面和大家分享软件开发中的前端开发和后端开发分类和各自的职责&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 前端开发&…

Gorm简单了解

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly. 04_GORM查询操作_哔哩哔哩_bilibili 前置&#xff1a; db调用操作语句中间加debug&#xff08;&#xff09;可以显示对应的sql语句 1.Gorm模型定义&#xff08;理解重点&#xff…

Unity 之 方括号[ ] 的用法以及作用

文章目录 在Unity中&#xff0c;方括号 [ ] 通常用于表示属性、特性&#xff08;Attributes&#xff09;或者元数据&#xff08;Metadata&#xff09;。这些标记提供了附加信息&#xff0c;可以用于修改类、方法、字段等的行为或者在编辑器中进行设置。 以下是一些常见的用法&…

笔试题目回忆

&#xff08;1&#xff09;给出n,k&#xff0c;n表示数组个数&#xff0c;k表示要剔除的个数&#xff0c;接下来n个数为数组元素&#xff0c;求剔除k个数之后&#xff0c;其他所有数互为倍数&#xff0c;每个数最多剔除一次。 未检测代码&#xff0c;超时。 #include <ios…

[dasctf]misc04

与他不说一模一样吧也差不多 第三届红明谷杯CTF-【MISC】-阿尼亚_keepb1ue的博客-CSDN客flag.zip需要解压密码&#xff0c;在图片中发现一串密文。一串乱码&#xff0c;尝试进行字符编码爆破。获取到密码&#xff1a;简单的编码。https://blog.csdn.net/qq_36618918/article/d…

基于springboot跟redis实现的排行榜功能(实战)

概述 前段时间&#xff0c;做了一个世界杯竞猜积分排行榜。对世界杯64场球赛胜负平进行猜测&#xff0c;猜对1分&#xff0c;错误0分&#xff0c;一人一场只能猜一次。 1.展示前一百名列表。 2.展示个人排名(如&#xff1a;张三&#xff0c;您当前的排名106579)。 一.redis so…

c#继承(new base)的使用

概述 C#中的继承是面向对象编程的重要概念之一&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为父类或基类&#xff09;继承属性和行为。 继承的主要目的是实现代码重用和层次化的组织。子类可以继承父类的字段、属性、方法和事…

nvidia-smi nvcc -V 及 CUDA、cuDNN 安装

nvidia-smi nvcc -V 及 CUDA、cuDNN 安装 1. 问题缘由2. 分析3. CUDA Driver API 安装3.1 Software & Updates3.2 官网下载 4. CUDA Runtime API 安装5. 安装 cuDNN5.1 cuDNN下载 6. 一点点小注意事项 1. 问题缘由 之前查找 CUDA 版本时都是直接使用的 nvidia-smi 指令&am…

OpenCV(十三):图像中绘制直线、圆形、椭圆形、矩形、多边形和文字

目录 1.绘制直线line() 2.绘制圆形circle() 3.绘制椭圆形ellipse() 4.绘制矩形rectangle() 5.绘制多边形 fillPoly() 6.绘制文字putText() 7.例子 1.绘制直线line() CV_EXPORTS_W void line(InputOutputArray img,Point pt1, Point pt2,const Scalar& color,int t…

2021年03月 C/C++(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:生日相同 2.0 在一个有180人的大班级中,存在两个人生日相同的概率非常大,现给出每个学生的名字,出生月日。试找出所有生日相同的学生。 时间限制:1000 内存限制:65536 输入 第一行为整数n,表示有n个学生,n ≤ 180。此后每行包…

论文阅读_扩散模型_DDPM

英文名称: Denoising Diffusion Probabilistic Models 中文名称: 去噪扩散概率模型 论文地址: http://arxiv.org/abs/2006.11239 代码地址1: https://github.com/hojonathanho/diffusion &#xff08;论文对应代码 tensorflow&#xff09; 代码地址2: https://github.com/AUTOM…

java八股文面试[数据库]——索引的基本原理、设计原则

索引的设计原则 索引覆盖是什么&#xff1a; 索引&#xff08;在MySQL中也叫做“键&#xff08;key&#xff09;”&#xff09; 是存储引擎用于快速找到记录的一种数据结构。这是索引的基本功能。 索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时&#xff0c;索引…

【小沐学Unity3d】3ds Max 多维子材质编辑(Multi/Sub-object)

文章目录 1、简介2、精简材质编辑器2.1 先创建多维子材质&#xff0c;后指定它2.2 先指定标准材质&#xff0c;后自动创建多维子材质 3、Slate材质编辑器3.1 编辑器简介3.2 编辑器使用 结语 1、简介 多维子材质&#xff08;Multi/Sub-object&#xff09;是为一个模形&#xff0…

使用Vue3和Vite升级你的Vue2+Webpack项目

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

使用PAM保障开发运营安全

硬编码凭据和 DevOps 系统中缺乏凭据安全性是组织的巨大漏洞。以明文形式访问凭据的恶意内部人员可以在 IT 中建立和扩展其立足点 基础设施&#xff0c;构成巨大的数据被盗风险。 什么是PAM 特权访问管理 &#xff08;PAM&#xff09; 是指一组 IT 安全管理原则&#xff0c;可…

解决gitee仓库中 .git 文件夹过大的问题

最近&#xff0c;许多项目都迁移到gitee。使用的也越来越频繁&#xff0c;但是今天突然收到一个仓库爆满的提示。让我一脸懵逼。本文将详细为你解答&#xff0c;这种情况如何处理。 1、起因 我收到的报错如下&#xff1a; remote: Powered by GITEE.COM [GNK-6.4] remote: T…