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指针,所以不能访问非静态成员变量

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


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

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

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

相关文章

如何在云端加速缓存构建

缓存是指将某类数据存储起来以便以后重复使用的过程&#xff0c;它的运用在开发场景中非常普遍。类似于你习惯把最常用的调料放在厨房台面上&#xff0c;而不是橱柜里&#xff0c;这样你在准备大餐时就可以轻松取用。 但对于一个更为技术性、更精确的用例&#xff0c;比如像谷…

linux 使用笔记

1.查看运行内存 a.Free 快速查看内存的方法&#xff0c;也是经常使用的命令&#xff0c; -h 更人性化的显示内存的单元 -m 以M的形式显示 b.Top Top命令提供了实时性的运行中的程序的资源使用统计。可以根据内存的使用和大小来进行排序。 如上所示&#xff0c;top命令可以看…

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

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

SpringBoot(三层框架Controller,Mapper,Service)中遇到的一些注解整理

本文主要从Controller层,Service层,Mapper层这三层架构中记录用到的各种注解 还有一些MyBatis用到的注解 持续更新到本人的毕设做完为止,太多了太多了根本学不完哈哈哈 1.Controller层 1.1GetMapping/PostMapping/DeleteMapping/PutMapping 用于建立HTTP请求与处理方法之间的…

Flutter中使用minio_new库

前言 在移动开发中&#xff0c;我们常常会遇到需要在App中处理文件上传和下载的需求。Minio是一个开源的对象存储服务&#xff0c;它兼容Amazon S3云存储服务接口&#xff0c;可以用于存储大规模非结构化的数据。 开始之前 在pubspec.yaml文件中添加minio_new库的依赖&#xf…

快乐学Python,使用Python为电视剧主演生成词云

上篇文章我们串联了爬虫技术的几个基础环节&#xff0c;将电视剧的信息保存到了 csv 文件。这里&#xff0c;我们做个小实验&#xff1a;将主演信息生成词云。&#xff08;其他文章可看专栏文章&#xff09; 1、需求描述 将全集网抽取的电视剧&#xff08;名称、评分、主演&a…

云服务器基于Centos创建个人云盘实践经验分享

文章目录 安装运行Cloudreve安装ossfscentos更换yum源 配置ossfs挂载oss存储配置开机启动 配置cloudreve推荐阅读 安装运行Cloudreve 执行如下命令&#xff0c;下载cloudreve安装包。 wget https://labfileapp.oss-cn-hangzhou.aliyuncs.com/cloudreve_3.3.1_linux_amd64.tar…

爬虫笔记(一):实战登录古诗文网站

需求&#xff1a;登录古诗文网站&#xff0c;账号&#xff0b;密码&#xff0b;图形验证码 第一&#xff1a;自己注册一个账号&#xff0b;密码哈 第二&#xff1a;图形验证码&#xff0c;需要一个打码平台&#xff08;充钱&#xff0c;超能力power&#xff01;&#xff09;或…

纯命令行在Ubuntu中安装qemu的ubuntu虚拟机,成功备忘

信息总体还算完整&#xff0c;有个别软件更新了名字&#xff0c;所以在这备忘一下 1. 验证kvm是否支持 ________________________________________________________________ $ grep vmx /proc/cpuinfo __________________________________________________________________…

【android】 android 里写jni

目录 &#xff08;1&#xff09; 环境准备 (2) 关联c文件到gradle文件 &#xff08;3&#xff09; 生成了 (4) 书写 &#xff08;5&#xff09; 使用 &#xff08;6&#xff09;业务调用 参考文档 &#xff08;1&#xff09; 环境准备 ndk, cmake (2) 关联c文件到gr…

three.js从入门到精通系列教程002 - three.js正交相机OrthographicCamera

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>three.js从入门到精通系列教程002 - three.js正交相机OrthographicCamera</title><script src"ThreeJS/three.js"></script><script src&qu…

Android Matrix绘制PaintDrawable设置BitmapShader,手指触点为圆心scale放大原图,Kotlin(二)

Android Matrix绘制PaintDrawable设置BitmapShader&#xff0c;手指触点为圆心scale放大原图&#xff0c;Kotlin&#xff08;二&#xff09; 在 Android Matrix绘制PaintDrawable设置BitmapShader&#xff0c;手指触点为圆心scale放大原图&#xff0c;Kotlin-CSDN博客 基础上&…

Vscode 上安装 Compilot

GitHub Copilot 是由 OpenAI 和 GitHub 开发的 AI 工具。其目的是通过自动完成代码来帮助开发人员使用集成开发环境 &#xff08;IDE&#xff09;&#xff0c;如 Visual Studio Code。它目前仅作为技术预览版提供&#xff0c;因此只有已在候补名单上被接受的用户才能访问它。对…

C# wpf 实现任意控件(包括窗口)更多调整大小功能

WPF拖动改变大小系列 第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能&#xff08;本章&#xff09; 文章目录 WPF拖动改变大小系列前言一、添加的功能1、任意控件Drag…

分布式ID(2):雪花算法生成ID

1 雪花算法简介 这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示: 41-bit的时间可以表示(1L&l…

汽车微电机行业研究:预计2029年将达到188亿美元

微电机行业是技术密集型行业&#xff0c;其起源于欧洲的德国、瑞士等国家&#xff0c;发展于日本。随着改革开放&#xff0c;中国作为发展中国家&#xff0c;承接了德国、日本等发达国家的汽车微电机产业转移&#xff0c;技术扩散逐步向我国转移。 微特电机广泛应用于信息处理设…

高清网络视频监控系统技术方案

目 录 一、概述 二、建设目标及需求 &#xff08;一&#xff09;建设总目标 &#xff08;二&#xff09;需求分析 三、设计依据与设计原则 &#xff08;一&#xff09;设计依据 &#xff08;二&#xff09;设计原则 四、建设方案设计 &#xff08;一&…

Vue3新特性defineModel()便捷的双向绑定数据

官网介绍 传送门 配置 要求&#xff1a; 版本&#xff1a; vue > 3.4(必须&#xff01;&#xff01;&#xff01;)配置&#xff1a;vite.config.js 使用场景和案例 使用场景&#xff1a;父子组件的数据双向绑定&#xff0c;不用emit和props的繁重代码 具体案例 代码实…

Unity导出Android项目踩坑记录

导出的时候需要注意以下地方的配置&#xff1a; 1、buildSetting-> 设置ExportProject 2、buildsetting ->playerSetting ->设置IL2CPP 3、设置ndk edit->preferences->external tools->ndk 如果unity的ndk版本和android项目里的ndk版本不一致会报错&…

css3+javaScript实现一个左右钟摆-摇晃的红灯笼网页特效

css3javaScript实现一个左右钟摆-摇晃的红灯笼网页特效&#xff01;前天逛博客时无意中看见了&#xff0c;别人的博客顶部有一个会左右钟摆的摇晃的红灯笼&#xff0c;产生了想法&#xff0c;我也想给自己做一个&#xff0c;但是网上找了很多方案&#xff0c;都没有实现。终于在…