C++类与对象(四):再谈构造函数(详解初始化列表)、Static成员

上次把默认的成员函数部分梳理完毕了:C++初阶类与对象(三):详解复制构造函数和运算符重载
今天接着讲下面的内容:


文章目录

  • 1.再谈构造函数
    • 1.1构造函数体赋值
    • 1.2初始化列表
      • 1.2.1格式和概念
      • 1.2.2由来
        • 情况1
        • 情况2
      • 1.2.3特性
      • 1.2.4特殊情况
    • 1.3explicit关键字
  • 2. static成员
    • 2.1概念与引入
    • 2.2特性


1.再谈构造函数

1.1构造函数体赋值

根据之前介绍的内容:在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值,我们之前使用的构造函数都叫做函数体内赋初值

class Date
{
public:
	Date(int year = 2024, int month = 1, int day = 1)//使用全缺省,也是默认构造函数
	{
		//函数体内初始化,在函数体内进行赋值
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;//变量声明
	int _month;
	int _day;
};

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

初始化与赋值区别:

  1. 初始化是在创建变量时为其赋予一个初始值。在构造函数中,初始化通常是在对象创建时对成员变量进行赋值。初始化可以在变量声明时进行,也可以在构造函数的初始化列表中进行(下面就介绍)。
  2. 赋值是在变量已经存在的情况下改变变量的值。赋值操作符=用于将一个值赋给一个已经存在的变量
  • 初始化是在变量创建时进行的,而赋值是在变量已经存在的情况下进行的
  • 初始化可以只进行一次,而赋值可以进行多次
  • 在一些情况下,初始化可能比赋值更加高效,因为它可以在对象创建时直接将初始值传递给对象,而不需要额外的操作

1.2初始化列表

1.2.1格式和概念

初始化列表:成员变量定义处

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

class Date
{
public:
	Date(int year = 2024, int month = 1, int day = 1)//使用全缺省,也是默认构造函数
		:_year(year)
		,_month(month)
		,_day(day)  //初始化列表
	{

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

那大家可能有疑问了? 之前函数体内赋值不是用的好好的嘛,来这个干嘛? 现在就来解释:

1.2.2由来

情况1
class Date
{
public:
	Date(int year = 2024, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)  //这三个可以在这,也可以在函数体内
		,_ref(year)
		,_n(1)  //这两个必须在这里
	{

	}
    //两个地方可以混着用,这样也行  
	//Date(int year = 2024, int month = 1, int day = 1)
	//	: _ref(year)
	//	, _n(1)  //这两个必须在这里
	//{
	//	_year = year;//没有在初始化列表显示出来定义,但是也会定义和初始化,不过内置类型随机值。
                                            //自定义类型调用自己的默认构造函数
	//	_month = month;
	//	_day = day;//这三个可以在这,也可以在初始化列表内
	//}
    
private:
	int _year;//这些都是声明,还没有空间
	int _month;
	int _day;

	int& _ref;//引用,必须在定义的时候初始化
	const int _n;//常量,必须在定义的时候初始化
};

int main()
{
	//实例化对象
	Date d1;//此时才定义,但是对象整体定义; 那每个成员在哪里定义呢?——就在初始化列表
	return 0;
}

可以知道的是:在进去函数体之前,定义和初始化都已经完成了,函数体进行的只是单纯的赋值操作。

所有的初始化行为都是在初始化列表内完成的。如果在初始化列表里没有出现的话一般是会在初始化列表给他初始化为默认值(随机值或自己给的缺省值)

之前我们也用过缺省值:

class Date
{
public:
	//两个地方可以混着用,这样也行
	Date(int year = 2024, int month =1, int day = 1)
		: _ref(year)
		, _n(1)  //这里给1,看结果
	{
		_year = year;
		_month = month;
		_day = day;//这三个可以进行赋值
	}
private:
	int _year=1;//只给_year缺省值
	int _month;
	int _day;

	int& _ref;//引用,必须在定义的时候初始化
	const int _n=2;//这里给2
};

如果在初始化列表里进行了显示地初始化,那就按照列表里进行(最优先); 没有那才会用缺省值;连缺省值都没有那就随机值了。

上述赋值结果:

请添加图片描述

情况2
class Stack
{
public:
	Stack(int capacity)
	{
		//.......
	}
	//没有默认构造函数了
private:
	int* _a;
	int _top;
	int _capacity;
};

class MyQueue
{
public:
	//此时都是自定义类型,但是又没有默认构造函数;或者有但是不想用。就要自己初始化
	MyQueue()
		:_s1(4)//自己显示地初始化
		, _s2(5)
	{

	}
private:
	Stack _s1;
	Stack _s2;
};

如果自定义类型没有默认构造函数。解决方案:

  1. 写出来默认构造
  2. 在初始化列表处显示地写出来

1.2.3特性

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:(在由来里讲了)
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化
  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
  3. 解决的问题
    • 必须在定义的地方显示地初始化:引用 const
    • 没有默认构造函数的自定义成员
    • 有些自定义成员想要自己控制自己的初始化

1.2.4特殊情况

class Stack
{
public:
	Stack(int capacity=3)
	{
		cout << "调用了Stack的默认构造函数";
		//.......
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

class MyQueue
{
public:

private:
	Stack _s1;
	Stack _s2;//这俩可调用Stack类的默认构造
	int _size = -1;//给了缺省值
	//此时可以简单点理解:该类的默认构造函数对于自定义就去调用他们的,对于内置使用缺省或随机值
};
class Stack
{
public:
	Stack(int capacity=3)
	{
		cout << "调用了Stack的默认构造函数";
		//.......
	}
	//没有默认构造函数了
private:
	int* _a;
	int _top;
	int _capacity;
};

class MyQueue
{
public:
	MyQueue()//有没有效果一样,没写就按照默认构造函数那老一套
	{ }  //写了初始化列表一定会走,但没有显示的写那也是老一套
private:
	Stack _s1;
	Stack _s2;//这俩可调用Stack类的默认构造
	int _size = -1;//给了缺省值
	
};

请添加图片描述

1.3explicit关键字

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

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

  1. 构造函数是单参数
class A
{
public:
	A(int a = 0)
		:_a(a)
	{ }
private:
	int _a;
};

int main()
{
	A a1(1);//这样创建对象大家都知道

	A a2 = 2;//这样也可以:内置类型对象隐式转换为自定义类型对象
	         //能这样做,是A的int单参数构造函数支持的
			 //其实隐式转换中间产生一个临时变量,临时变量是A类型的

	const A& ra = 3;//给临时变量起别名,这时临时变量会在引用的作用域结束时销毁
	return 0;
}

请添加图片描述

  1. 除第一个参数无默认值其余均有默认值的多参构造函数
class Date
{
public:
	 Date(int year, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{ }

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

int main()
{
	Date d1(2024, 1, 6);
    Date d2=2024;//
    //Date d2 = { 2024,1,1 };这样也行
	Date d3 = (2024, 1, 1);//这样是逗号表达式子<==> Date d3=1 <==> Date d3=(Date)1 <==> Date d3(1)
	return 0;
}

请添加图片描述

  1. 全缺省构造函数
class Date
{
public:
	// explicit修饰构造函数,禁止类型转换
	 Date(int year=1, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{ }

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

int main()
{
	Date d1;
	Date d2 = { 2024,2 };//从左到右依次赋值
	Date d3 = { 2024,2,2 }; //这样也行
	Date d4 = 2024;
	return 0;
}

请添加图片描述


2. static成员

2.1概念与引入

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

用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化(不走初始化列表,不属于单个成员。类里声明,类外定义)

static静态成员变量:属于整个类,属于这个类所有对象。受访问限定符限制

实际上:静态成员函数和静态成员变量,本质上是受限制的全局变量和全局函数(专属这个类,受类域和访问限定符的限制)

#include<iostream>
using namespace std;
class A
{
public:
	A()//无参构造
	{
		count++;
	}
	A(A& a)//拷贝构造
	{
		count++;
	}

	static int count;//类内声明,属于整体(公有)
};
int A::count = 0;//类外定义,就类似于成员函数声明和定义分离

int main()
{
	A aa;
	cout << A::count << endl;//正常大家会想到这样访问
	cout << aa.count << endl;//这样也可以,类比调用成员函数:告诉编译器去那个类里找
}

此时是公有,那如果是私有。要怎么访问呢???

对于count都在类外定义了,为什么不能直接访问呢? 这样就直接以成员函数类比就行

using namespace std;
class A
{
public:
	A()//无参构造
	{
		count++;
	}
	A(A& a)//拷贝构造
	{
		count++;
	}
	int getCount()
	{
		return count;
	}
private:
	static int count;//类内声明,属于整体(私有)
};
int A::count = 0;//类外定义

int main()
{
	A aa;
	cout << aa.getCount()-1 << endl;//因为为了得到count而特地创建了一个对象来调用get函数,要-1;
}

现在count是私有了,就定义了一个getCount函数来得到。但是:为了得到count而特地创建了一个对象来调用get函数(还是有点不合适)

对于对象调用成员函数意义:1. 是告诉编译器getCount在A类里 2. 另一个是传this指针

而编译器在编译阶段遇到变量或者函数,都会去找出处,向上找和全局找(也是命名空间和类域起作用原因)

class A
{
public:
	A()//无参构造
	{
		count++;
	}
	A(A& a)//拷贝构造
	{
		count++;
	}
	static int getCount()//静态成员函数,没有this指针,所以不能访问非静态成员变量
	{
		return count;
	}
private:
	static int count;//类内声明,属于整体(公有)
};
int A::count = 0;//类外定义

int main()
{
	A aa;
	cout << A::getCount()-1 << endl;//可以直接用类名调用
    cout << aa.getCount << endl;//这样也行
}	

2.2特性

根据上面也可总结出一些特性:

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。它们在类的所有实例之间是唯一的。因此,静态成员函数可以直接访问静态成员变量,因为它们不依赖于特定的对象实例,而是与整个类相关联
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
  6. 静态成员函数,没有this指针,所以不能访问非静态成员变量

实际上:静态成员函数和静态成员变量,本质上是受限制的全局变量和全局函数(专属这个类,受类域和访问限定符的限制)


这次就先到这里啦,下次类与对象的内容也要告一段落了,感谢大家支持!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言第三弹---数据类型和变量

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 数据类型和变量 1、数据类型介绍1.1、整型1.2、浮点型1.3、字符型1.4、布尔类型1.5、各种数据类型的长度1.5.1、sizeof操作符1.5.2、数据类型的长度1.5.3、sizeo…

全网最详细!!Python 爬虫快速入门

1. 背景 最近在工作中有需要使用到爬虫的地方&#xff0c;需要根据 Gitlab Python 实现一套定时爬取数据的工具&#xff0c;所以借此机会&#xff0c;针对 Python 爬虫方面的知识进行了学习&#xff0c;也算 Python 爬虫入门了。 需要了解的知识点&#xff1a; Python 基础语…

【蓝桥杯EDA设计与开发】立创开源社区分享的关于蓝桥被EDA真题与仿真题的项目分析

立创开源社区内有几个项目分享了往年 EDA 设计题目与仿真题&#xff0c;对此展开了学习。 【本人非科班出身&#xff0c;以下对项目的学习仅在我的眼界范围内发表意见&#xff0c;如有错误&#xff0c;请指正。】 项目一 来源&#xff1a;第十四届蓝桥杯EDA赛模拟题一 - 嘉立…

JS-WebAPIs-其他事件(三)

• 页面加载事件 页面加载事件主要有二种事件&#xff0c;分别是load和DOMContentLoaded 加载外部资源&#xff08;如图片、外联CSS和JavaScript等&#xff09;加载完毕时触发的事件为什么要学&#xff1f; 有些时候需要等页面资源全部处理完了做一些事情老代码喜欢把 scrip…

Git教程学习:01 Git简介与安装

目录 1 版本控制1.1 什么是版本控制系统&#xff1f;1.2 本地版本控制系统1.3 集中式版本控制系统1.4 分布式版本控制系统 2 Git简史3 Git的安装3.1 在Linux上安装3.2 初次运行Git前的配置 1 版本控制 1.1 什么是版本控制系统&#xff1f; 版本控制系统(Version Control Syst…

LLaVA-Plus: Learning to Use Tools for Creating Multimodal Agents

LLaVA-Plus: Learning to Use Tools for Creating Multimodal Agents 最近在调研一些多模态大模型相关的论文&#xff0c;发现Arxiv上出的论文根本看不过来&#xff0c;遂决定开辟一个新坑《一页PPT说清一篇论文》。自己在读论文的过程中会用一页PPT梳理其脉络和重点信息&#…

【2023】java使用WebClient实现chatGPT调用建立web socket连接

&#x1f4bb;目录 一、介绍1、使用技术2、效果 二、代码1、前端代码2、后端代码2.1、maven依赖2.2、model2.2.1、请求接口的格式2.2.2、响应数据对象 2.3、工具类2.3.1、&#x1f534;使用WebClient调用chatgpt方法2.3.2、&#x1f7e0; webSocket连接对话方法 2.4、Controlle…

【微信小程序开发】环境介绍和基本使用

文章目录 前言1. 项目的基本组成结构1.1 JSON 配置文件的作用1.2 如何新建小程序页面1.3 修改项目首页1.4 WXML 模板1.5 WXSS 样式1.6 JS 逻辑交互 2. 宿主环境2.1 什么是宿主环境2.2 通信模型2.3 运行机制2.4 组件2.4.1 view 组件的基本使用&#xff1a;2.4.2 scroll-view 组件…

【数据结构与算法】1.时间复杂度和空间复杂度

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

vue3前端开发,生命周期函数的基础练习

vue3前端开发,生命周期函数的基础练习&#xff01; 下面先给大家看一个图片&#xff0c;帮助大家了解&#xff0c;vue3的生命周期函数&#xff0c;和旧版本vue2的生命周期函数&#xff0c;有什么变化。 如图所示&#xff0c;vue3里面&#xff0c;把前面2个函数&#xff0c;混在…

展锐T618_虎贲T618紫光展锐安卓核心板规格参数

基于紫光展锐八核T618平台的纯国产化方案&#xff0c;采用了开放的智能Android操作系统&#xff0c;并集成了4G网络、2.5G5G双频WIFI(可支持1*1 MIMO)、BLUETOOTH近距离无线传输技术以及GNSS无线定位技术。用户可以根据特定场合的需求&#xff0c;选择合适的嵌入式ARM核心模块&…

禅道的安装及使用

文章目录 1.禅道的下载安装2.禅道管理员管理账户3.禅道管理产品角色操作4.禅道关联需求 1.禅道的下载安装 1、禅道下载网址&#xff1a;http://www.zentao.net/ 2、下载好之后把该文件放到D盘上 3、双击点开然后点击”Extract“进行解压该文件 4、解压中 5、解压完就会出现…

Git学习笔记(第5章):Git团队协作机制

目录 5.1 团队内协作 5.2 跨团队协作 Git进行版本控制都是在本地库操作的。若想使用Git进行团队协作&#xff0c;就必须借助代码托管中心。 5.1 团队内协作 问题引入&#xff1a;成员1&#xff08;大佬&#xff09;利用Git在宿主机上初始化本地库&#xff0c;完成代码的整体…

Oracle 12CR2 RAC部署翻车,bug避坑经历

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

【原文链接】Tri-Perspective View for Vision-Based 3D Semantic Occupancy Prediction

原文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Huang_Tri-Perspective_View_for_Vision-Based_3D_Semantic_Occupancy_Prediction_CVPR_2023_paper.pdf 1. 引言 体素表达需要较大的计算量和特别的技巧&#xff08;如稀疏卷积&#xff09;&…

Java(spring cloud)智慧工地(项目层+工地层+APP)源码

智慧工地提供工地智能管理服务&#xff0c;打通数据壁垒&#xff0c;互通管理中心各平台。实现&#xff1a;“可视”、“可控”、“可管”。智慧工地管理云平台是一种利用人工智能和物联网技术来监测和管理建筑工地的系统。它可以通过感知设备、数据处理和分析、智能控制等技术…

chatgpt国内使用网站(免费收藏级)

如果您认为本文对你有帮助&#xff0c;希望可以点赞收藏&#xff01;感谢您的支持 下面我为你推荐我自己在用的gpt类工具&#xff0c;帮你在工作学习生活上解决一些大小问题 &#x1f389;智能GPT 地址&#xff1a; https://meet.adminjs.net 在他的详情中有详细的使用介绍&am…

统信UOS_麒麟KYLINOS安装JDBC驱动包

原文链接&#xff1a;统信UOS/麒麟KYLINOS安装JDBC驱动包 亲爱的读者们&#xff0c;大家好&#xff01;今天&#xff0c;我为大家带来一篇非常实用的技术文章——在统信UOS和麒麟KYLINOS操作系统上&#xff0c;如何使用Dbeaver连接Oracle数据库。Dbeaver是一个广泛使用的数据库…

工业设备管理系统:助力企业实现数字化转型

随着工业4.0和智能制造的快速发展&#xff0c;数字化转型已成为企业提升竞争力、适应市场变化的必然选择。工业设备管理系统作为数字化转型的关键组成部分&#xff0c;能够为企业提供实时监控、数据分析、预警和远程控制等功能&#xff0c;助力企业实现数字化转型的目标。 一、…

list上

文章目录 初步了解list面试题&#xff1a;为什么会有list&#xff1f;vector的缺点&#xff1a;vector、list优点 list结构迭代器的分类list的简单运用insert、erase、迭代器失效&#xff08;和vector的区别&#xff09;erase class和structlist的迭代器为什么这个迭代器的构造…