C++类和对象:初始化列表、static成员和友元

news2024/9/25 9:29:42

目录

一. 初始化列表

1.1 对象实例化时成员变量的创建及初始化

1.2 初始化列表

1.3 使用初始化列表和在函数体内初始化成员变量的效率比较

1.4 成员变量的初始化顺序

1.5 explicit关键字

二. static成员

2.1 static属性的成员变量

2.2 static属性的成员函数

三. 友元

3.1 友元函数

3.2 友元类


一. 初始化列表

1.1 对象实例化时成员变量的创建及初始化

以演示代码1.1中的日期类Date为例,Date类中有一个用户显示定义的构造函数,来实现在类实例化时对成员变量赋初值。但是,成员变量并不是在构造函数的函数体内部进行创建的,在进入构造函数的函数体内部之前,成员变量会先被给一个随机的值,然后到构造函数内部改变这个随机的值来让成员变量初始化为用户希望的值。

因此,我们可以认为,Date成员变量是先被创建出来(不进行初始化),然后在被赋初值的。

演示代码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 初始化列表

先将成员变量创建出来,再初始化,相比于创建时直接初始化,效率相对较低,而且,对于引用、具有const属性以及没有默认构造函数的自定义类型成员变量,其必须在定义创建的同时完成初始化。因此,在函数体内部初始化成员变量就有其局限性。

为了实现在定义创建成员变量的同时进行初始化,C++引入了初始化列表。其语法格式为:以一个冒号开始(冒号在函数声明的后面),以逗号分隔成员变量,每个成员变量后面跟一个括号表示被初始化的值。

演示代码1.2通过初始化列表,实现了对日期类三个成员变量的初始化。

演示代码1.2:

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

注意,下面三种类型的成员变量只能采用初始化列表进行初始化:

  1. 引用类型成员变量。
  2. 具有const属性的成员变量。
  3. 没有默认构造函数的自定义类型成员变量。

演示代码1.2:(三种只能使用初始化列表的成员变量的初始化)

class A
{
public:
	A(int a)
		:_a(a)
	{}

private:
	int _a;
};

class B
{
public:
	B(A& a, int& ref)
		//没有默认成员函数的内置类型、引用以及const属性成员变量
		//只能用初始化列表初始化
		: _a1(a)
		, _ref(ref)
		, _n(10)
	{}

private:
	A _a1;   //没有默认成员函数的内置类型
	int& _ref;  //引用
	const int _n;  //const属性成员变量
};

1.3 使用初始化列表和在函数体内初始化成员变量的效率比较

演示代码1.3创建一个含有默认构造函数的类AA,在类A中包含类型为AA的自定义成员变量,先后显示定义两种不同形式的默认构造函数:一是在函数体内部初始化自定义成员变量,二是在初始化列表初始化自定义类型成员变量。

为了方便观察两种情况下构造函数、拷贝构造函数和赋值运算符重载函数分别调用了几次,函数应输出相关信息。运行代码,可以看到,如果在函数体内部初始化,需要调用两次调用构造函数、一次调用赋值运算符重载函数,如果使用初始化列表初始化,仅需要调用构造函数和拷贝构造函数各一次。对于较大的自定义类型成员变量,使用初始化列表可以显著提高程序运行效率。

这是因为,使用初始化列表在成员变量被定义出来的同时完成了初始化,而在函数体内部初始化则将成员变量的定义和初始化拆分为两个步骤。

演示代码1.3:

class AA
{
public:
	AA(int a = 1, int b = 2)  //构造函数
	{
		cout << "AA(int a, int b)" << endl;
		_a = a;
		_b = b;
	}

	AA(const AA& aa)  //拷贝构造函数
	{
		cout << "AA(AA& aa)" << endl;
		_a = aa._a;
		_b = aa._b;
	}

	AA& operator=(const AA& aa)  //赋值运算符重载函数
	{
		cout << "AA& operator=(const AA& aa)" << endl;
		_a = aa._a;
		_b = aa._b;

		return *this;
	}

private:
	int _a;
	int _b;
};

class A
{
public:
	//函数体内初始化
	//A(int c, int d, const AA& aa)  //这里会调用初始化列表创建_aa,但给随机初值
	//{
	//	_c = c;
	//	_d = d;
	//	_aa = aa;
	//}

	//初始化列表初始化
	A(int c, int d, const AA& aa)
		: _c(c)
		, _d(d)
		, _aa(aa)
	{}

private:
	int _c;
	int _d;
	AA _aa;
};

int main()
{
	AA aa1;
	A a1(10, 20, aa1);
	return 0;
}
图1.1  在函数体内初始化(左)和采用初始化列表初始化(右)时程序运行结果

总结:对于有默认构造函数的自定义类型成员变量,可以在函数体内部初始化,也可以使用初始化列表初始化,但为了提高程序运行的效率,一般建议采用初始化列表初始化。

1.4 成员变量的初始化顺序

成员变量初始化的顺序与声明顺序一致,与初始化列表的顺序无关。如演示代码1.4所示,虽然初始化列表中_a1在前_a2在后,但是_a2依然先被初始化,_a1初始化后的值并没有被赋给_a2。因此,程序的运行结果并不是1  1,而是1  随机值 。

建议:成员变量的声明顺序与初始化列表的顺序保持一致。

演示代码1.4:

class AA
{
public:
	AA(int a1 = 10, int a2 = 20)
		:_a1(1)
		, _a2(_a1)   //按照声明顺序,先初始化_a2再初始化_a1
	{}

	void Print()
	{
		cout << "_a1 = " << _a1 << endl;
		cout << "_a2 = " << _a2 << endl;
	}

private:
	int _a2;
	int _a1;
};

int main()
{
	AA aa(1,1);
	aa.Print();
	return 0;
}
图1.2  演示代码1.4的运行结果

1.5 explicit关键字

如果一个构造函数只有一个参数,或者除了第一个参数外每个参数都有默认的缺省值,那么,就可以通过隐式类型转换来实现对象的实例化。

演示代码1.5定义了一个类Year,通过Year y1 = 2023,实现通过隐式类型转换完成对象实例化。但是,某些时候我们不希望这种隐式类型转换的发生,C++为此提供了一个关键字explicit,在函数声明之前添加explicit,即可禁止这种隐式类型转换。

演示代码1.5:

class Year
{
public:
	//函数前加explicit,禁止隐式类型转换
	explicit Year(int year = 2023);

	void Print();

private:
	int _year;
};

Year::Year(int year)  //构造函数
	: _year(year)
{
	cout << "Year(int year = 2023)" << endl;
}

void Year::Print()
{
	cout << _year << endl;
}

int main()
{
	Year y1(2020);
	//Year y2 = 2023;   //存在隐式类型转换
	y1.Print();
	//y2.Print();
	return 0;
}

二. static成员

2.1 static属性的成员变量

  • 静态(static)成员变量属于整个类,并非单独属于某个类对象,而是所有类对象共享。
  • 必须在类外部定义static成员变量和给定初始化值,在类内部只是声明static成员变量。

通过static成员变量,可以实现统计一个类被实例化的次数。如演示代码2.1所示,在类A中定义了一个static成员变量_s_count,每当构造函数或拷贝构造函数被调用时,执行++_s_count操作,表示创建了一个类对象。运行演示代码2.1,_s_count的值为3,表示进行了3次对象实例化,调用构造函数创建对象a1第一次、调用构造函数创建对象a2第二次、调用f()函数传参时拷贝构造形参第三次。

注意:static属性的成员变量存储在静态区,在使用sizeof计算类或类对象大小时,static成员不包含在其中。

演示代码2.1:(统计类实例化次数)

//统计类对象总共被创建的次数
//构造函数和拷贝构造函数都会创建类对象
class A
{
public:
	A(int a = 5)   //构造函数
		: _a(a)
	{
		cout << "A(int a = 5)" << endl;
		++_s_count;
	}

	A(A& a)   //拷贝构造函数
		: _a(a._a)
	{
		cout << "A(A& a)" << endl;
		++_s_count;
	}

	//非静态成员函数
	int GetSumCount()
	{
		return _s_count;
	}

private:
	int _a;
	static int _s_count;
};

int A::_s_count = 0;   //初始化静态成员变量

void f(A a)
{ }

int main()
{
	A a1;
	A a2 = 1;
	f(a1);

	//通过对象调用函数获取_s_count的值
	cout << "_s_count = " << a1.GetSumCount() << endl;   

	return 0;
}
图2.1 演示代码2.1的运行结果

2.2 static属性的成员函数

static属性的成员函数严格来说并不能算是类的成员函数,其没有this指针,不能访问非static属性的成员变量或调用非static属性的成员函数。

静态成员函数有两种访问方式:(1)对象名.函数名(参数列表)、(2)类名::函数名(参数列表)

  • static成员也受访问限定符的限制,不能在类外部访问private和protect属性的static成员。
  • 虽然静态成员函数不能调用非静态成员函数,但是非静态成员函数可以调用静态成员函数。
class A
{
public:
	static void GetNum();   //静态成员函数

private:
	int _a1 = 10;
	static int _a2;
};

int A::_a2 = 20;   

void A::GetNum()
{
	//cout << _a1 << endl;   //_a1为非静态成员变量,静态成员函数不能访问
	cout << "_a2 = " << _a2 << endl;
}

int main()
{
	A a;
	A::GetNum();  //通过类名调用static属性函数
	A().GetNum();   //通过对象调用static属性函数,A()为匿名对象
	return 0;
}

三. 友元

友元可以分为友元函数和友元类。有一点要事先声明:友元是一种严重破坏封装的行为,一般建议友元要少用慎用。

3.1 友元函数

对于一个类中的private或protect属性成员,全局函数不能访问它们,但是,如果我们将某个全局函数声明为类的友元函数,那么这个全局函数就可以不受访问限定符的约束,随意访问类中的成员变量和成员函数。

友元函数的声明形式:friend 返回类型 函数名(参数列表)

演示代码3.1展示了友元函数的定义和使用,在类A中声明Print为A的友元,则全局函数Print可以访问A的私有属性成员变量并输出其值。这里还有一点需要注意:友元函数并不是某个类的成员函数,它是属于全局的。

演示代码3.1:

class A
{
	friend void Print(A& a);   //声明Print为A的友元
public:
	A(int a1 = 10, int a2 = 20)  //构造函数
		: _a1(a1)
		, _a2(a2)
	{}

private:
	int _a1;
	int _a2;
};

void Print(A& a)
{
	cout << "_a1 = " << a._a1 << endl;
	cout << "_a2 = " << a._a2 << endl;
}

int main()
{
	A a;
	Print(a);  //Print为类A的友元
	return 0;
}
图3.1 演示代码3.1的运行结果

3.2 友元类

如果定义一个类A为类B的友元,那么这个类B中的任意一个成员函数就可以访问类A中的private或public属性成员。需要注意的是,友元类没有传递性和交互性,即:

  • 如果B是A的友元,C又是B的友元,不能认为C就是A的友元。
  • 假设B是A的友元,B可以访问A的非公有属性成员,我们也不能认为A是B的友元,即一般不能在类A中访问类B的非公有属性成员。

友元类的声明方式:friend class 类名

演示代码3.2定义了一个日期类Date和一个时间类Time,声明Date类是Time类的友元,那么,我们就可以在Date类中定义一个SetTimeOfDay函数,来设置Date类的Time类型成员变量。进入调试界面,打开监视窗口,看到Time类型成员_t的每个成员变量的值都依据调用SetTimeOfDay时传递的参数改为了12。

演示代码3.2:

class Time
{
	friend class Date;  //友元类声明

public:
	Time(int hour = 1, int minute = 1, int second = 1)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int year = 2023, int month = 3, int day = 2)  //构造函数
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	//Date类的成员函数实现对Time类非公有属性成员的访问
	void SetTimeOfDay(int hour, int minute, int second)
	{
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d;  //使用默认值完成类实例化
	d.SetTimeOfDay(12, 12, 12);  //时间设为12时12分12秒
	return 0;
}
图3.2 演示代码3.2

 

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

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

相关文章

废气处理设备远程监控

当今工业迅速的发展&#xff0c;工业带给人们的经济效益显著&#xff0c;而同时污染问题也备受关注。国家环保标准对排放至大气的废气指标提出了更高的要求。面临着环保压力&#xff0c;企业为走可持续发展之路&#xff0c;为维护员工利益、改善工作环境及周边环境不受影响&…

一、Sping框架引入

OCP开闭原则 什么是OCP&#xff1f; OCP是软件七大开发原则当中最基本的一个原则&#xff1a;开闭原则 对什么开&#xff1f;对扩展开放。 对什么闭&#xff1f;对修改关闭。OCP原则是最核心的&#xff0c;最基本的&#xff0c;其他的六个原则都是为这个原则服务的。OCP开闭原则…

计算机行业回暖?看网友怎么说?

就业寒潮之下&#xff0c;去年的应届生们可谓哀嚎一片&#xff0c;不少人晒出自己的0offer秋招战绩。 就连过去无往不利的计算机行业&#xff0c;亦不例外。但今年开始&#xff0c;计算机行业逐渐有了回暖的迹象和讨论。 陆续有不少之前哭诉收获惨淡的计算机专业同学&#x…

防静电和浪涌TVS layout设计要点

电子产品精密化刚看过了CES2023&#xff0c;雷卯的外贸伙伴们看了最新的AR,VR,5G产品&#xff0c;新的电子产品更智能、更复杂&#xff0c;嵌入了脆弱和敏感的集成电路。这些设备的环境往往很恶劣&#xff0c;产生高水平静电和快速瞬态浪涌。这些ESD事件可能会干扰设备&#xf…

IIS之web服务器的安装、部署以及使用教程(图文详细版)

WEB服务器的部署 打开虚拟机后查看已经开放的端口&#xff0c;可以看到没有TCP 80、TCP 443&#xff0c;说明HTTP服务端口没有打开 打开我的电脑—双击CD驱动器 选择安装可选的Windows组件 选择应用程序服务器—打开Internet信息服务—选择万维网服务和FTP服务 一路确…

uniapp-首页配置

为了获取到后台服务器发来的数据&#xff0c;需要配置相应的网络地址。位置在main.js入口文件中。 import { $http } from escook/request-miniprogramuni.$http $http // 配置请求根路径 $http.baseUrl https://api-hmugo-web.itheima.net// 请求开始之前做一些事情 $http.…

Spring-Xml配置

一、Spring 简介 1.简介 文档下载地址&#xff1a;Index of /spring-framework/docs 1.简介 Spring framework 是 Spring 基础框架 学习Spring 家族产品 Spring framework SpringBoot SpringCloud Spring 能用来做什么 开发 WEB 项目 微服务 分布式系统 Spring framew…

云服务HCIE变题当天一把过!分享下学习备考和考试经验

大家好&#xff0c;我是誉天云服务学员刘同学。感谢在誉天的学习&#xff0c;让我在临考变题的情况下通过了云服务HCIE考试&#xff1b;也感谢誉天给我这次机会分享出学习备考和考试的经验。 算起来&#xff0c;我和誉天也是老朋友了&#xff1a;一开始是跟着邹老师学习云计算、…

【JAVA】线程和进程

&#x1f3c6;今日学习目标&#xff1a;线程和进程 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人主页&#xff1a;颜颜yan_的个人主页 ⏰本期期数&#xff1a;第三期 &#x1f389;专栏系列&#xff1a;JAVA 线程和进程前言一、进程与线程1.进程2.线程二、线程的创建2.1 继…

Pytest自动化框架~权威教程03-原有TestSuite的执行方法

前言TestSuite一直是unittest的灵活与精髓之处, 在繁多的测试用例中, 可以任意挑选和组合各种用例集, 比如smoke用例集, level1用例集, webtest用例集, bug回归用例集等等, 当然这些TestSuite需要我们提前定义好, 并把用例加载进去.Pytest采取的是完全不同的用例组织和运行方式…

有些笑话,外行人根本看不懂,只有程序员看了会狂笑不止

我一直都觉得我们写代码的程序员与众不同&#xff0c;就连笑话都跟别人不一样。 如果让外行人来看我们一些我们觉得好笑的东西&#xff0c;他们根本不知道笑点在哪里。 不信你来瞧瞧&#xff0c;但凡有看不懂的地方&#xff0c;说明你的道行还不够深。 1.大多数人开始学编程时…

论文解读14——XGBoost:A Scalable Tree Boosting System

目录1、文章贡献2、算法推导3、寻找分裂点算法3.1 精确贪心算法3.2 近似算法4、稀疏感知算法5、特征维度的并行化6、XGBoost VS GBDT7、XGBoost局限半年前看了这篇XGBoost的原文&#xff0c;网上解读很多&#xff0c;于是迟迟没有将其中的精髓记录下来&#xff0c;准备重点记一…

从工厂打螺丝到月薪18k测试工程师,我该满足吗?

以前我比较喜欢小米那句“永远相信美好的事情即将发生”&#xff0c;后来发现如果不努力不可能有美好的事情发生&#xff01;01高中毕业进厂5年&#xff0c;创业经商多次战败&#xff0c;为了生计辗转奔波高中毕业后我就进了工厂&#xff0c;第一份工作是做模具加工。从500元一…

1.基于Label studio的训练数据标注指南:信息抽取(实体关系抽取)、文本分类等

文本抽取任务Label Studio使用指南 1.基于Label studio的训练数据标注指南&#xff1a;信息抽取&#xff08;实体关系抽取&#xff09;、文本分类等 2.基于Label studio的训练数据标注指南&#xff1a;&#xff08;智能文档&#xff09;文档抽取任务、PDF、表格、图片抽取标注等…

网络资源面经2

文章目录Kafka 原理&#xff0c;数据怎么平分到消费者生产者分区消费者分区Flume HDFS Sink 小文件处理Flink 与 Spark Streaming 的差异&#xff0c;具体效果Spark 背压机制具体实现原理Yarn 调度策略Spark Streaming消费方式及区别Zookeeper 怎么避免脑裂&#xff0c;什么是脑…

用Python自己写一个分词器,python实现分词功能,隐马尔科夫模型预测问题之维特比算法(Viterbi Algorithm)的Python实现

☕️ 本文系列文章汇总&#xff1a; &#xff08;1&#xff09;HMM开篇&#xff1a;基本概念和几个要素 &#xff08;2&#xff09;HMM计算问题&#xff1a;前后向算法 代码实现 &#xff08;3&#xff09;HMM学习问题&#xff1a;Baum-Welch算法 代码实现&#xff08…

【数据结构】关于二叉树你所应该知道的数学秘密

目录 1.什么是二叉树&#xff08;可以跳过 目录跳转&#xff09; 2.特殊的二叉树&#xff08;满二叉树/完全二叉树&#xff09; 2.1 基础知识 2.2 满二叉树 2.3 完全二叉树 3.二叉树的数学奥秘&#xff08;主体&#xff09; 3.1 高度与节点个数 3.2* 度 4.运用二叉树的…

算法拾遗二十五之暴力递归到动态规划五

算法拾遗二十七之暴力递归到动态规划七题目一【数组累加和最小的】题目二什么暴力递归可以继续优化暴力递归和动态规划的关系面试题和动态规划的关系如何找到某个问题的动态规划方式面试中设计暴力递归的原则知道了暴力递归的原则 然后设计常见的四种尝试模型如何分析有没有重复…

力扣-丢失信息的雇员

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1965. 丢失信息的雇员二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他…