【C++】:类和对象(3)

news2024/11/25 9:48:33

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关C++的基础知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

 

目录

引言:

1. 初始化列表

1.1 构造函数体赋值

1.2 初始化列表

1.3 explicit关键字

2. static成员 

2.1 概念

2.2 特性

3. 友元

3.1 友元函数

3.2 友元类

4. 内部类

5. 匿名对象

6. 拷贝对象时编译器做出的优化


引言:

本期我们继续承接上篇的类和对象(2)继续对类和对象进行收尾工作,在前面我们首先对类和对象做了基本介绍以及深入了解,然后自己实现了一个日期类,关于类和对象还有遗留的知识点,本期再来进行介绍。

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成员变量
  • 自定义类型成员(且该类没有默认构造函数时)
class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a;
};

class B
{
public:
	B(int a, int ref)
		:_aobj(a)
		, _ref(ref)
		, _n(10)
	{}
private:
	A _aobj; // 没有默认构造函数
	int& _ref; // 引用
	const int _n; // const
};

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

class Time
{
public:
	Time(int hour = 0)
		:_hour(hour)
	{
		cout << "Time()" << endl;
	}
private:
	int _hour;
};

class Date
{
public:
	Date(int day)
	{}
private:
	int _day;
	Time _t;
};

int main()
{
	Date d(1);
    return 0;
}

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

class A
{
public:
	A(int a1, int a2, int a3)  //根据声明的次序来进行初始化
		:_a1(a1)  //2
		,_a2(a2)  //1
		,_a3(a3)  //3
	{}
private:
	int _a2;
	int _a1;
	int _a3;
};

int main()
{
	A a(1, 2, 3);
	return 0;
}

总结:

不管写不写初始化列表,每一个成员都要走初始化列表,内置类型用随机值处理,自定义类型去调用去调用它的默认构造,,C++11支持在声明时给缺省值,这个值给初始化列表。

初始化列表使用场景:

1. 必须在定义时初始化的量

2. const修饰的常变量

3. 引用值

4. 自定义类型成员且无默认构造 

1.3 explicit关键字

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

class A
{
public:
	A(int i)
	//explicit A(int i)  //加上explicit可以阻止这种隐式类型的转化
		:_a(i)
	{
		cout << "A(int i)" << endl;
	}

	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

int main()
{
	A a1(1);
	A a2 = 2; //从int转化为A是一种隐式类型转化
	// 用2调用A构造函数生成一个临时对象,再用这个对象去拷贝构造a2
	// 编译器会再优化,优化用2直接构造

	const A& ra = 2; //类型转化会产生临时变量,而临时变量具有常性,需要使用const修饰
	return 0;
}

用explicit修饰构造函数,将会禁止构造函数的隐式转换。 

2. static成员 

2.1 概念

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

2.2 特性

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

//统计累计创建了多少个对象
class A
{
public:
	A() 
	{ 
		++_scount;
		++_scountCount;
	}

	A(const A & t) 
	{ 
		++_scount; 
		++_scountCount;

	}

	~A() 
	{ 
		--_scount; 
	}

	static int GetA_scount()
	{ 
		return _scount; 
	}
	static int GetA_scountCount()
	{
		return _scountCount;
	}
private:
	//声明
	static int _scount;  //正在使用的对象
	static int _scountCount;  //累计创建的对象
};
//定义
int A::_scount = 0;
int A::_scountCount;

A Func(A aa)
{
	return aa;
}

int main()
{
	A a1, a2;   //创建两个对象

	Func(a2);   //拷贝构造时创建一个,返回值又创建一个

	cout << A::GetA_scount() << endl;
	cout << A::GetA_scountCount() << endl;
	return 0;
}

3. 友元

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

3.1 友元函数

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

class Date
{
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, 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& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1;
	cin >> d1;
	cout << d1;
	return 0;
}

说明:

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

3.2 友元类

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

  • 友元关系是单向的,不具有交换性。
  • 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
  • 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  • 友元关系不能传递:如果C是B的友元, B是A的友元,则不能说明C时A的友元。
  • 友元关系不能继承(后续解析)
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;
};

4. 内部类

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


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

  • 1. 内部类可以定义在外部类的public、protected、private都是可以的。
  • 2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • 3. sizeof(外部类)=外部类,和内部类没有任何关系。
    // 1、B类受A类域和访问限定符的限制,其实他们是两个独立的类
    // 2、内部类默认就是外部类的友元类
    class A
    {
    public:
    	class B // B天生就是A的友元
    	{
    	public:
    		void print(const A& a)
    		{
    			cout << k << endl;   //可以直接访问A的静态成员变量
    			cout << a.h << endl; //也可以访问A的成员变量
    		}
    	};
    private:
    	static int k;
    	int h;
    };
    int A::k = 1;
    
    int main()
    {
    	A::B b;
    	b.print(A());
    	cout << sizeof(A) << endl;  //外部类的大小与内部类无关
    	return 0;
    }

5. 匿名对象

匿名对象的特点就是不用取名字,生命周期只存在定义的这一行。

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 aa1();// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	A();// 但是他的生命周期只有这一行,紧接着它的下一步就会自动调用析构函数

	A aa2(2);
	return 0;
}

应用场景: 当我们做C++的OJ题时会发现都是将其封装在一个Solution类中的,假设我们需要调用这个类中的某一个函数,是需要先创建一个Solution的对象,然后通过这个对象进行调用,这样的话有点麻烦,我们可以直接使用匿名对象来调用这个类中的成员函数。

class Solution {
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};
int main()
{
	// 1.基本方法
	Solution sl;
	sl.Sum_Solution(10);

	// 2.匿名对象
	Solution().Sum_Solution(10);

	return 0;
}

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.coloradmin.cn/o/1097700.html

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

相关文章

技术实现数据获取技巧

在移动App数据爬取中&#xff0c;HTTP抓包和脱壳技术是两种常用的手段&#xff0c;能够帮助我们获取App中的数据。在本文中&#xff0c;我将与大家分享关于移动App数据爬取的技术实现&#xff0c;包括HTTP抓包和脱壳数据获取技巧。希望能对你在移动App数据爬取方面有所帮助&…

选择OLED透明拼接屏的五大理由:品质、技术、参数、功能、应用

OLED透明拼接屏作为一款引领未来显示技术的创新产品&#xff0c;以其独特的特点和卓越的显示效果在市场上备受瞩目。 在这篇文章中&#xff0c;编者将为您详细介绍OLED透明拼接屏的厂商背景、特点、显示优势、订购流程、价位范围以及售后服务&#xff0c;帮助您了解这一产品更…

2023最新Python学习路线+百部python基础视频

前几天整理了一份python从入门到精通实战的百 万字教程PDF&#xff0c; 反响不错 那么趁火打劫&#xff0c;不是&#xff0c;是趁热打铁。整理了一套关于python基础入门的视频教程。 视频文字结合观看效果更佳的哦 内容依然是从入门到进阶&#xff0c;既有视频教程又有文档资…

使用AI来跟踪动物行为,现在科技界都这么牛了吗!?

原创 | 文 BFT机器人 运动为了解大脑如何运作和控制身体提供了一个窗口。从剪贴板和笔观察到基于现代人工智能的技术&#xff0c;追踪人类和动物的运动已经取得了长足的进步。当前的尖端方法利用人工智能来自动跟踪身体各部分的移动。然而&#xff0c;训练这些模型仍然非常耗时…

《永恒空间2》v1.1.36252中文版

游戏介绍 《EVERSPACE 2》是一款快节奏的单人太空射击游戏&#xff0c;融合了经典的RPG元素&#xff0c;具有高度探索性且提供大量战利品。在纯手工搭建的开放世界中体验惊险刺激的故事&#xff0c;探索无尽的奥秘&#xff0c;在成为人类的道路上克服艰难险阻。 游戏截图 www…

为分布式系统设计数据库

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 数据库设计是微服务和云原生解决方案的关键因素&#xff0c;因为基于微服务的架构导致了数据的分布式。数据管理不再在一个单一的过程中发生&#xff0c;而是可以通过多…

VMware安装windows7系统

目录 一、准备工作 二、新建虚拟机 三、安装系统 四、win7虚拟机更改为固定/静态IP 一、准备工作 下载镜像&#xff0c;选择win7-x64专业版&#xff0c;复制ed2k链接&#xff0c;使用迅雷下载&#xff08;非常快&#xff09;&#xff0c;其它win系统安装方法大同小异&#xff…

大模型学习路线与建议

文章目录 第一章 深度学习基础第二章 智能对话系统基础第三章 大模型基础第四章 大模型应用实践第五章 大模型实战项目 第一章 深度学习基础 深度学习基础 深度学习经典模型解析 深度学习模型优化策略解析 深度学习GPU原理与应用方法 深度学习GPU并行训练策略解析 深度学习模型…

STM32如何使用PWM?

一&#xff1a;PWM介绍 PWM 是 Pulse Width Modulation 的缩写&#xff0c;中文意思就是脉冲宽度调制&#xff0c;简 称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有 效的技术&#xff0c;其控制简单、灵活和动态响应好等优点而成为电力电子技术最广…

橙河网络:国外答题问卷赚钱可靠吗?

大家好&#xff0c;我是橙河网络&#xff0c;今天聊一聊国外答题问卷赚钱可靠吗&#xff1f;这几年&#xff0c;很多人都了解到海外问卷调查这个项目&#xff0c;这是一个类似网络搬砖的项目&#xff0c;可以自己做&#xff0c;现实中也有很多老板选择组建工作室运营这个项目。…

看了B站上的这些课程,我飘了!

众所周知&#xff0c;B 站是一个神奇的网站&#xff0c;上面有各种各样的视频资源&#xff0c;可以说是包罗万象、应有尽有。很多人在上面跟着 UP 主上自习、学编程、考研&#xff0c;还真的能学有所成。 最近&#xff0c;很多小伙伴后台私信知了姐&#xff0c;想要一些开发、网…

软信天成:今天的企业比以往任何时候都更需要「数据编目」

在当今的组织中&#xff0c;数据是多样的&#xff0c;分布在许多不同的部门、应用系统、数据仓库和数据湖&#xff08;一些在内部&#xff0c;其他在云中&#xff09;&#xff0c;因此&#xff0c;准确地知道您拥有哪些数据和它们在哪里是一个挑战。此外&#xff0c;另外一个挑…

Linux系统中如何开启和配置OpenGauss数据库的远程连接

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…

基于变电站自动化系统中的安全措施分析及应用

摘要&#xff1a;阐述变电运行中的问题&#xff0c;电气自动化系统与安全运行措施&#xff0c;包括自动控制设备的投入&#xff0c;电气自动 化与计算机技术相、设备数据的采集与处理、自动化系统的升级、人工智能技术的应用。 关键词&#xff1a;自动控制&#xff1b;数据采…

浅谈安科瑞多回路仪表在德国数据中心的应用

摘要&#xff1a;数据中心是一个聚集了大量服务器、存储设备、网络设备及配套UPS、空调等设备的IT设备场所&#xff0c;是实现数据信息的集中处理、存储、传输、交换和集中管理等业务的服务平台。 数据中心供电电源质量的好坏直接影响到IT设备的安全运行&#xff0c;因此对数据…

SQ4840EY-T1_GE3具有低导通电阻和低电压降 汽车级 N沟道功率MOSFET

SQ4840EY-T1_GE3是一款高性能的车规级电子IC芯片&#xff0c;它具有多种功能和特点&#xff0c;适用于各种电子设备和应用领域。采用了先进的工艺技术&#xff0c;具有高性能和稳定的特点。它采用了先进的封装技术&#xff0c;能够在广泛的温度范围内正常工作&#xff0c;适应各…

Jmeter接口自动化测试 —— Jmeter下载安装及入门

jmeter简介 Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试&#xff0c;但后来扩展到其他测试领域。 下载 下载地址&#xff1a;Apache JMeter - Download Apache JMeter 安装 由于Jmeter是基于Java的…

146.LRU缓存

双向链表哈希表 class LRUCache { public://1、定义双向链表结构、容量、哈希表等LRU数据成员struct Node{int key,value;Node *left,*right;Node(int _key,int _value):key(_key),value(_value),left(NULL),right(NULL){}}*L,*R;int n;unordered_map<int,Node*> ump;//…

基于Debian12打造的LMDE 6 “Faye” 正式发布

导读LMDE 6 已正式发布&#xff0c;代号 "Faye"&#xff1b;新版本基于 Debian 12 Bookworm。 LMDE 6 已正式发布&#xff0c;代号 "Faye"&#xff1b;新版本基于 Debian 12 Bookworm。 LMDE 是 "Linux Mint Debian Edition" 的缩写&#xff0c…

进来“抄作业”!示例代码、操作手册,尽在华为云Codelabs!

1 Codelabs 简介 1.1 什么是 Codelabs&#xff1f; Codelabs 是华为云开发者工具&#xff0c;提供互动式的&#xff0c;以实践为主的教程&#xff0c;这些教程旨在指导开发者通过实际操作来学习新的编程技能、工具、框架。华为云 Codelabs 提供丰富的华为云产品代码示例/操…