【C++初阶】--类和对象(下)

目录

一.const成员

 1.权限放大问题

2.权限的缩小 

二.再谈构造函数 

1.构造函数体赋值 

2.初始化列表 

(1)概念 

(2)使用 

①在对象实例化过程中,成员变量先依次进行初始化

②再进行函数体内二次赋值 

3.explicit关键字 

(1)C++为什么要存在自动隐式类型转换这样的东西呢?

 (2)explicit关键字

​编辑

(3)知识补充:多参数的函数进行隐式类型转换

(4)总结:缺省值的各种写法

三.static成员 

1.概念 

2.使用 

①面试题:A类型创建了多少个对象? (统计构造了多少次) 

四.友元 

1.友元函数 

2.友元类 

五.内部类 

1.概念 

2.特性 

六.匿名对象 


一.const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

 1.权限放大问题

class Date
{
public:Date()//构造函数{_year = 1;_month = 1;_day = 1;}Date(int year=1,int month=1,int day=1)//带参构造函数(函数重载){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{const Date d1(2024, 1, 31);d1.Print();return 0;
}

这里存在一个权限被放大的问题: 

  • 优化 
//权限的平移
void Print() const
{cout << _year << "-" << _month << "-" << _day << endl;
}

2.权限的缩小 

class Date
{
public:Date()//构造函数{_year = 1;_month = 1;_day = 1;}Date(int year=1,int month=1,int day=1)//带参构造函数(函数重载){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d2(2024, 3, 31);d2.Print();return 0;
}

  • 注意 

权限的放大只有指针和引用才存在,而拷贝是不影响的。

	//可以运行const int i = 0;int j = i;//报错const int* p1 = &i;int* p2 = p1;

二.再谈构造函数 

1.构造函数体赋值 

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。  

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

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

2.初始化列表 

(1)概念 

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

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

(2)使用 

  • 注意事项

类中包含以下成员,必须放在初始化列表位置进行初始化:

  1. 引用成员变量
  2. const成员变量
  3. 自定义类型成员(且该类没有默认构造函数时)  
 class A
{
public://A(int a = 0, int b = 1)A(int a, int b):_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),_aa(1, 2),_p((int*)malloc(sizeof(4) * 10)){if (_p == nullptr){perror("malloc fail");}for (size_t i = 0; i < 10; i++){_p[i] = 0;}}private:// 声明int _year;int _month;int _day;// 必须走初始化const int _n;int& _ref;A _aa;int* _p;
};

①在对象实例化过程中,成员变量先依次进行初始化
  • 图示解析 

②再进行函数体内二次赋值 

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

3.explicit关键字 

  • 知识引入 

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。 

class C
{
public:C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}private:int _x;
};int main()
{C cc1(1);// 单参数构造函数支持隐式类型的转换C cc2 = 2;//2 用来构造一个临时对象,再进行拷贝构造-> 编译器优化了,同一个表达式连续步骤的构造,一般会被合二为一return 0;
}

  • 回顾:一个临时变量不能引用 

(1)C++为什么要存在自动隐式类型转换这样的东西呢?

  • 传统进行栈的插入 
class Stack
{
public:void Push(const C& c){//}
};
int main()
{Stack st;C cc4(3);st.Push(cc4);return 0;
}

在插入该类时,我们还需要在创建一个该类型的对象,非常麻烦。 

  • 有了自动隐式类型转换后的栈插入
class Stack
{
public:void Push(const C& c){//}
};
int main()
{Stack st;C cc4(3);st.Push(4);return 0;
}

这里我们将4直接传给了st.Push(),而该成员函数的参数类型是自定义类型,但由于存在隐式类型的自动转换,我们可以直接传过去了!非常爽!

 (2)explicit关键字

有些地方我们并不想发生隐式类型的自动转换,这是添加explicit关键字就可以解决这个问题了。

class C
{
public:explicit C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}private:int _x;
};int main()
{C cc1(1);C cc2 = 2;return 0;
}

(3)知识补充:多参数的函数进行隐式类型转换

class A
{
public://explicit A(int a1, int a2)A(int a1, int a2):_a1(a1),_a2(a2){}private:int _a1;int _a2;
};int main()
{//隐式类型转换A aa1 = { 1, 2 };const A& aa2 = { 1, 2 };//构造的话就正常构造A aa2(2,3)return 0;
}

(4)总结:缺省值的各种写法

class B
{
private:// 缺省值int _a = 1;int* _p = (int*)malloc(4);A aa1 = { 1,2 };
};
  • 小题试炼 

以下程序的最终结果是什么? 

A.输出1 1
B.程序崩溃 

C.编译不通过
D.输出1 随机值

class A
{
public:A(int a):_a1(a), _a2(2){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:// 声明顺序int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}

答案:D 

  • 解析

初始列表初始的顺序不是该列表出现的顺序(也就是先_a1再_a2),而是声明的顺序(先_a2再_a1)。

三.static成员 

1.概念 

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

2.使用 

①面试题:A类型创建了多少个对象? (统计构造了多少次) 

  • 写法一:直接统计出构造和拷贝构造的总次数
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;
}

由于各个编译器的优化程度不一样,所以导致最后调用的次数可能会不同,因此这种写法具有一定的风险性。 

  • 写法2:设置成静态成员,并突破类域和访问限定符 
class A
{
public:A(){++n;}A(const A& aa){++n;}//不是属于某一个对象,属于所有对象,属于整个类static int n ;//在类内声明
};
int A::n = 0;//在类外定义
A Func()
{A aa;return aa;
}int main()
{A aa1;A aa2;Func();//三种访问形式cout << aa1.n << endl;cout << aa2.n << endl;cout << A::n << endl;return 0;
}
  • 写法3:存在访问限定符 
class A
{
public:A(){++n;}A(const A& aa){++n;}int GetN(){return n;}
private://不是属于某一个对象,属于所有对象,属于整个类static int n ;//在类内声明
};
int A::n = 0;//在类外定义
A Func()
{A aa;return aa;
}int main()
{A aa1;A aa2;Func();cout << aa1.GetN() << endl;return 0;
}
  • 写法4:静态成员函数写法 
class A
{
public:A(){++n;}A(const A& aa){++n;}//静态成员函数没有this指针static int GetN(){return n;}
private://不是属于某一个对象,属于所有对象,属于整个类static int n ;//在类内声明
};int A::n = 0;//在类外定义
A Func()
{A aa;return aa;
}int main()
{A aa1;A aa2;Func();cout << A::GetN() << endl;return 0;
}

四.友元 

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

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

1.友元函数 

问题:现在尝试去重载operator,然后发现没办法将operator重载成成员函数。因为cout的 输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。 

class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 << cout  ---->  d1.operator<<(&d1, cout); 不符合常规调用// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧ostream& operator<<(ostream& _cout){_cout << _year << "-" << _month << "-" << _day << endl;return _cout;}
private:int _year;int _month;int _day;
};

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

class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
int main()
{Date d;cin >> d;cout << d << endl;return 0;
}
  • 注意事项 
  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同  

2.友元类 

  1. 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
  2. 友元关系是单向的,不具有交换性。 (比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。)
  3. 友元关系不能传递 如果C是B的友元, B是A的友元,则不能说明C时A的友元。
  4. 友元关系不能继承。  
class Time
{friend class Date;// 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
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 = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};

五.内部类 

1.概念 

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

  • 注意

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

class A
{
public://内部类天生就是外部类的友元类class B{public:void func(A* p){p->_a1++;}};
private:int _a1;int _a2;
};int main()
{cout << sizeof(A) << endl;A::B bb;
}

2.特性 

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3.  sizeof(外部类)=外部类,和内部类没有任何关系。  
  • 内部类的大小 
class A
{
public:class B{private:int _b1;};
private:int _a1;int _a2;
};int main()
{cout << sizeof(A) << endl;//8
}
  1. 类B受类A的类域限制,所以类A里其实是没有类B的,类B可以理解为和全局变量没有区别。
  2. 类B在创建对象时受到类A封装的限制。
int main()
{A::B bb;//这样创建对象
}
如果将类B设置成private,则无法访问

六.匿名对象 

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{// 有名对象A aa1;// 匿名对象// 特点:生命周期只在当前一行A();A(10);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

从概念到实现:开发一款招聘APP的技术指南

如今&#xff0c;招聘APP作为连接求职者和招聘企业的重要工具&#xff0c;其功能和性能直接影响着用户体验和市场竞争力。本文将带领读者深入探讨从概念到实现的全过程&#xff0c;为开发一款优秀的招聘APP提供技术指南。 一、理解市场需求与用户行为 在开发招聘APP之前&#…

Android BitmapDrawable.bitmap与BitmapFactory.decodeResource获取不到原始图像素级真实宽高,Kotlin

Android BitmapDrawable.bitmap与BitmapFactory.decodeResource获取不到原始图像素级真实宽高&#xff0c;Kotlin 当一个图片放在ImageView里面后&#xff0c;用以下方式获取图的宽高&#xff1a; val bmp1 (this.drawable as BitmapDrawable).bitmapLog.d("fly", &…

C语言——实用调试技巧——第2篇——(第23篇)

坚持就是胜利 文章目录 一、实例二、如何写出好&#xff08;易于调试&#xff09;的代码1、优秀的代码2、示范&#xff08;1&#xff09;模拟 strcpy 函数方法一&#xff1a;方法二&#xff1a;方法三&#xff1a;有弊端方法四&#xff1a;对方法三进行优化assert 的使用 方法五…

【C++STL】STL容器详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

VMware虚拟机从一台电脑复制到另一台电脑

1 概述 在一台电脑上利用虚拟机安装了OS系统&#xff0c;特别是如果虚拟机中的系统进行了各种繁琐的配置&#xff0c;因为换电脑或者需要在其他电脑上配置&#xff0c;这个时候就可以将虚拟机中的系统复制拷贝一份到新电脑上&#xff0c;省时省力。 2 操作步骤 2.1 vmx文件 …

Python学习 --- 面向对象

1.什么是对象 1.Python中创建类的关键字是 class 2.类的成员方法 1.函数是写在类外面的,方法则是写在类里面的 1.上面这一段代码中就展示了如何在方法中访问类的成员变量: self.成员变量名 3.魔术方法 魔术方法其实就是python中的类中的内置方法,下面这几个只是我们比较常…

高级语言期末2011级B卷

1.编写函数&#xff0c;实现按照如下公式计算的功能。其中n为自然数。 #include <stdio.h>int fac(int n) {if(n0)return 1;elsereturn n*fac(n-1); }double func(int n) {if(n0)return 0;return 1.0*n/((n1)*fac(n2))func(n-1); } 2.编写函数&#xff0c;对给定的整数数…

Flutter Engine 编译

本地环境 Flutter 开发基本环境配置&#xff0c;SDK【】 MAC. M2芯片 git工具 python环境[MAC自带] xcode Chromium depot_tools depot_tools 是调试 Flutter 引擎的必备工具包&#xff0c;包含了 gclient、gn 和 ninja 等工具&#xff0c;这些在下面会用到&#xff01;…

线段树学习笔记 下

可持久化线段树 上面两篇是几年前写的&#xff0c;笔者今日才加以整理&#xff0c;如有错误请见谅。 线段树加上版本就是可持久化线段树。 Problem Intro 给定一个数组&#xff0c;只需要单点修改和单点查询&#xff0c;但要维护版本。 具体说&#xff0c;每一次操作可能从…

中兴助力低空经济发展,携山东移动完成5G-A通感一体商用验证

日前&#xff0c;中兴通讯在5G-A通感一体化技术研究和商用落地领域实现新突破。具体来说&#xff0c;中兴通讯联手山东移动&#xff0c;率先完成了5G-A&#xff08;5G-Advanced&#xff09;通感一体化技术试点&#xff0c;完成对低空无人机的通信感知融合测试。据悉&#xff0c…

Linux使用C语言获取进程信息

Linux使用C语言获取进程信息 Author: OnceDay Date: 2024年2月22日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可查看专栏: Linux实践记录_Once_day的博客-CSDN博客 参考文档: Linux proc目录详解_/proc/mounts-CSDN博客Linux下/proc目录介绍 - 知乎 (zhihu.com)Linux内…

普中51单片机学习(AD转换)

AD转换 分辨率 ADC的分辨率是指使输出数字量变化一个相邻数码所需输入模拟电压的变化量。常用二进制的位数表示。例如12位ADC的分辨率就是12位&#xff0c;或者说分辨率为满刻度的1/(2^12)。 一个10V满刻度的12位ADC能分辨输入电压变化最小值是10V1/(2^12 )2.4mV。 量化误差 …

基于协同过滤算法的体育商品推荐系统

摘要 本文深入探讨了基于协同过滤算法的体育商品推荐系统的构建方法及其在电子商务中的重要性。首先&#xff0c;介绍了协同过滤算法的基本原理&#xff0c;包括用户-商品矩阵、相似度度量和推荐生成。其次&#xff0c;探讨了协同过滤算法在体育商品推荐中的两种主要应用方式&a…

Java知识点一

hello&#xff0c;大家好&#xff01;我们今天开启Java语言的学习之路&#xff0c;与C语言的学习内容有些许异同&#xff0c;今天我们来简单了解一下Java的基础知识。 一、数据类型 分两种&#xff1a;基本数据类型 引用数据类型 &#xff08;1&#xff09;整型 八种基本数…

Qt Android sdk配置报错解决

使用的jdk8总是失败&#xff0c;报错command tools run以及platform sdk等问题。后来主要是设置jdk版本为17&#xff0c;就配置生效了。Android sdk路径可以选用Android Studio自带的&#xff0c;但是也要在Qt中点击“设置SDK”按钮做必要的下载更新等。 编译器这里会自动检测到…

28-k8s集群中-StatefulSets控制器(进阶知识)

一、statefullsets控制器概述 1&#xff0c;举例 假如&#xff0c;我们有一个deployment资源&#xff0c;创建了3个nginx的副本&#xff0c;对于nginx来讲&#xff0c;它是不区分启动或者关闭的先后顺序的&#xff0c;也就是“没有特殊状态”的一个服务&#xff0c;也成“无状…

使用yolo-seg模型实现自定义自动动态抠图

yolov8导航 如果大家想要了解关于yolov8的其他任务和相关内容可以点击这个链接&#xff0c;我这边整理了许多其他任务的说明博文&#xff0c;后续也会持续更新&#xff0c;包括yolov8模型优化、sam等等的相关内容。 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; …

java——特殊文件日志技术

目录 特殊文件Properties文件XML文件XML文件有如下的特点XML的作用和应用场景解析XML文件 日志技术概述日志技术的体系结构Logback日志框架概述快速入门核心配置文件logback.xml日志级别项目中使用日志框架 特殊文件 Properties文件 后缀为.properties的文件&#xff0c;称之…

Qt MDI应用方法:QMdiArea和QMdiSubWindows类

重点&#xff1a; 1.使用MDI应用程序&#xff0c;需要在主窗口的工作区放置一个QMdiArea组件。 并将QMdiArea组件设置成中心窗口 2.MDI有两个显示模式&#xff1a;Tab多页显示模式和子窗口显示模式 子窗口显示模式有两种显示方法&#xff1a;窗口级联展开和平铺展开 窗口级联…

Vue+SpringBoot打造超市自助付款系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 商品类型模块2.2 商品模块2.3 超市账单模块 三、界面展示3.1 登录注册模块3.2 超市商品类型模块3.3 超市商品模块3.4 商品购买模块3.5 超市账单模块 四、部分源码展示4.1 实体类定义4.2 控制器接口 五、配套文档展示六、…