类和对象(下)

news2024/11/15 11:57:12

       关于类和对象依旧有许多难点,这篇博客将会讲解关于类的构造函数的初始化列表,静态成员,友元,内部类,以及匿名对象等一些比较复杂的东西。

初始化列表

我们之前就已经学过类和对象的构造函数,但是实际上那并不算是对象的初始化,其实算是给成员变量赋值。

而我们用c语言和c++时都知道,变量初始化只能初始化一次,而赋值可以在函数内赋值无数次

既然说构造函数只能算是给成员变量赋值,那么怎样才能证明?

根据之前学的知识,我们都知道用const修饰的对象都只能在初始化的时候确定值

那么我们用默认构造函数初始化const成员变量不就可以证明了吗?

我们直接看看:

发现用默认构造函数实际上不能初始化用const修饰的变量

这就变相的证明了默认构造函数内部实际上不是初始化,而是赋值

那么类的对象在哪里才算是初始化呢?

这就轮到初始化列表出场了。

不过在了解初始化列表之前需要先了解下初始化列表的使用场景。

初始化列表使用场景:

1.用来初始化const成员变量

2.用来初始化无默认构造函数的自定义类型成员变量

3.用来初始化引用成员变量

所谓初始化列表,实际上就是在构造函数下,以一个冒号为开始成员之间以逗号隔开的列表每一个成员变量后面用括号跟上赋的值

初始化列表的使用方式:

我们可以看到,确实是成功的初始化了成员变量。

而上面也说了,初始化列表不仅能够初始化const成员变量;

也可以修饰其它两种变量,其中,自定义类型的变量需要好好了解一下。

class B {
public :
	B(int _b)
	{
		b = _b;
	}
private:
	int b;
};

class A {
public:
	
	A()
		:a(10)
		,_b(5)
	{
	}
private:
	const int a;
	B _b;
};

int main()
{
	A _a;

	return 0;
}

 通过这里我们可以发现,实际上初始化列表在初始化自定义类型的变量的时候;

会调用对应的构造函数,并且将括号里的数据用来初始化成员变量,而若是初始化列表没有显示初始化自定义类型的成员变量时,就会调用默认构造函数,无则报错;

此外,还有引用的成员变量需要使用初始化列表才能用。

 这里我们可以看到,成功的初始化了rc这个引用类型的成员变量。

初始化列表的规则

对象的所有成员都会走一套初始化列表,面对自定义类型的变量,若是初始化列表没有显示初始化就会调用对应的默认构造函数,无则报错,而面对内置类型,有显示初始化就用显示的值,无则用随机值或者构造函数内部的数值。

此外,还有一个规则

初始化列表的初始化顺序是根据成员变量的声明顺序决定的。

 这样我们发现,成员变量的声明顺序是 先a2后a1,而初始化列表则是先a1后a2,这就说明,初始化列表的初始化顺序由变量的声明顺序决定。

explicit 关键字

之前我们写过Date类,而Date类中的构造函数其实还有其他用处。

比如有一个构造函数只有一个参数,或者说只有第一个参数是没有缺省值的时候,会出现隐式类型转换

我们先来看看代码。

 我们发现,Date类型的对象d1居然能够直接用int类型的常量来初始化。

实际上这里涉及到类的隐式类型转换。

首先编译器会将用构造函数创建一个Date类型的中间变量,再用拷贝构造将中间变量的值给d1

当然,实际上这只是便于理解的说法,现在的编译器都对这个过程进行了优化。

过程变成了直接用构造函数来将2022作为参数来构造d1。

下图可证:

这样实际上用处并不大,因为大部分的类的成员变量都不只有一个,比如Date类就有三个成员变量。

当然,我们也可以这样初始化一个变量,不过格式需要注意,如下:

 就好像是初始化数组一样,这样也是可以的,但是这样会影响可读性,因此c++针对这个出现了一个关键字——explicit。

将explicit关键字放于构造函数之前,就可以禁止这样的隐式类型转换。

我们可以看到,对象实例化的地方出现了报错。

因此这样就可以避免这种错误出现。

静态成员变量以及函数(static)

在类中,有一种特殊的成员变量——static成员变量,这种被称为静态成员变量,那么这种变量有何妙用呢?接下来我们就来深入了解一下吧。

 在之前我们学习过,static修饰的变量和普通变量不一样,普通变量都是在栈区,除非你是动态开辟的那么就在堆区,而static修饰的则在静态区。

而类中的成员若是用static修饰会怎样呢?

static修饰的成员特性

1.静态成员为所有对象共享,存放在静态区。

2.静态成员变量必须在类外定义,定义时不用加static关键字。

3.类的静态成员可以直接用类名::静态成员或者对象.静态成员来访问。

4.静态成员函数没有隐藏的this指针

5.静态成员也受限定符限制

了解了静态成员的特性后,我先来直接看看实现。

#include<iostream>

using  namespace  std;

class Date {
private:
	int _year;
	int _month;
	int _day;

	static int time;

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

	static void Print1()
	{
		cout << "静态成员函数" << endl;
	//	Print2();静态成员函数不能调用非静态成员函数,因为非静态成员函数必须在对象初始化后才能使用
	}
	void Print2()
	{
		cout << "非静态成员函数" << endl;
		Print1();//而非静态成员函数可以调用静态成员函数
	}
};

int Date::time = 1;

int main()
{
	Date::Print1();
	Date d1;
	cout << endl << endl;
	d1.Print2();
  

	return 0;
}

 

我们发现,静态成员函数不能调用非静态成员函数,而非静态成员函数则 能调用静态成员函数

此外,定义的time的static类型的变量只能在外面才能初始化,并且构造成员函数无法初始化静态成员变量,但是限定符依旧能够限制外部直接访问time这个静态成员变量。

友元

友元函数

之前我们实现了Date类,但是我们还有几个方法没有实现。

比如用istream直接输入Date变量,而不是通过构造函数来创建。

当然,我们可以在类里面实现,但是这样我们就会有隐藏的this指针,我们的输入就会变成这样:

Date d1;
d1>>cin;

这样就和cin不同了,因此 为了可读性,我们只能在类外面实现这种函数。

但是我们Date类的成员变量又有访问限定符private来防止外部直接访问,那么我们该怎么办呢?

这里就轮到友元出场了。

class Date {
	friend istream& operator>>(istream& in, Date& d);
	friend ostream& operator<<(ostream& out, const Date& d);

private:
	int _year;
	int _month;
	int _day;


public:
	static int time;


	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	static void Print1()
	{
		cout << "静态成员函数" << endl;
		//	Print2();静态成员函数不能调用非静态成员函数,因为非静态成员函数必须在对象初始化后才能使用
	}
	void Print2()
	{
		cout << "非静态成员函数" << endl;
		Print1();//而非静态成员函数可以调用静态成员函数
	}
};

int Date::time = 1;

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << '/' << d._month << '/' << d._day << endl;
	return out;
}

int main()
{
	Date d;
	cin >> d;
	cout << d;
	return 0;
}

看过实现后,再来看看友元函数的特性:

友元类的特性

1.友元函数可以直接访问类的私有和保护成员,但不是类的成员函数

2.友元函数不能用const修饰

3.友元函数可以在类的任意位置定义不受类的访问限定符限制

4.一个函数可以是多个类的友元

5.友元函数的调用和普通函数的调用原理相同

友元类

友元可不止只有友元函数可以使用,实际上,也有友元类存在。

而友元类实际上差不多。

class Time {
	friend class Date;

private:
	int _hour;
	int _minte;
public:
	Time()
	{

	}
	void Print()
	{
		cout << _hour << _minte << endl;
	}
};


class Date {
private:
	int _year;
	int _month;
	int _day;
	Time t;
public:
	Date()
	{

	}
	void Print()
	{
		cout << _year << _month << _day << t._hour << t._minte << endl;
	}
};

我们在Date类创建了一个Time类型的成员变量t,我们就能够直接访问 t 的成员变量。

友元类的特性

1.友元关系是单向的,不具有交换性。
2.友元关系不能传递
3.友元类不能继承

内部类

当我们在一个类的内部定义了另一个类,那么这个类就是内部类。

而这个内部类实际上就是相当于外部类的友元函数,因此内部类可以直接使用外部类的变量。

但是这个友元只是单向的,外部类并不能用内部类的成员。


class A {
private:
	int a;
	static int k;
public:
	class B {
	private:
		
	public :

		void Print(const A& d)
		{
			cout << d._a << endl;    
            cout << k << endl;
		}
	};

};

此外,内部类受外部类的访问限定符和类域限制。

内部类的特性:

1. 内部类可以定义在外部类的 publicprotectedprivate 都是可以的。
2. 注意内部类可以直接访问外部类中的 static成员,不需要外部类的对象/ 类名
3. sizeof( 外部类)= 外部类,和内部类没有任何关系。

匿名对象

 

在c++中有这样一种奇怪的对象,它没有名字,被称为匿名对象,我们直接看看如何实现。

class A {
private:
	int _a;
public:
	A(int a)
		:_a(a)
	{

	}
	~A()
	{
		cout << "这是一个析构函数" << endl;
	}
};

int main()
{
	A a1(1);

	A(1);
	return 0;
}

这个匿名对象十分神奇,它的生命周期只有这一行。

 

那么匿名对象有什么用呢?

比如,我们需要用类的方法返回一个值。

比如这样的类:

class Solution {
private:
	int n;
public:
	Solution(int _n)
		:n(_n)
	{

	}

	int addFromTo(int from,int to)
	{	
		int ret = 0;
		for (int i = from; i <= to; i++)
		{
			ret += i;
		}
		return ret;
	}
};

int main()
{
	Solution d(5);
	cout << d.addFromTo(0, 100) << endl;
	return 0;
}

 

 

我们为了计算从0到100的和而创造了一个对象。

但是这个对象可能之后都用不着了;

这时候就可以用匿名对象了。

编译器的优化

在一些新一点的编译器中,对象的创建会被编译器优化。

我们先创建一个这样的类。


class A {
private:
	int _a;
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "这是一个构造函数" << endl;
	}


	~A()
	{
		cout << "这是一个析构函数" << endl;
	}

	A(const A& a)
	{
		cout << "这是拷贝构造" << endl;
	}
};

1.优化场景1

 

int main()
{
	A a = 1;

	return 0;
}

在以前的编译器中,这里的对象应该是先构造一个中间变量再用中间变量拷贝构造出a。

顺序本来是这样的:

 但是现在是这样了:

 我们可以看到这里只有一次构造函数。

2.优化场景2

int f(A a)
{

}

int main()
{
	f(A());

	return 0;
}

 像这里,如果是用匿名对象来传参,就会直接优化,

变成一次构造函数:

 3.优化场景3

A f2()
{
	A a;
	return a;
}

int main()
{
	A ret = f2();


	return 0;
}

 像这样的场景,本来应该是构造加拷贝构造再拷贝构造的。

 而这里编译器会做一个优化,免去中间的拷贝构造,化为一个拷贝构造。

 这就是编译器的优化。

以上就是类的剩余知识了,谢谢大家。

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

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

相关文章

[小技巧]C# 反射

文章目录定义Namespace场景示例简单反射一个对象进行操作反射一个有参构造函数的对象反射一个泛型类型的对象简单反射一个方法进行操作反射一个泛型方法进行操作反射一个静态&#xff08;Static&#xff09;方法定义 反射提供描述程序集、模块和类型的对象&#xff08;Type 类…

设计模式----工厂模式

设计模式----工厂模式 文章目录设计模式----工厂模式一.简介1. 什么是工厂模式&#xff1f;2. 工厂模式的类型&#xff1f;3. 工厂模式的使用场景&#xff1f;二. 使用1. 简单工厂模式2. 工厂方法模式3. 抽象工厂模式一.简介 1. 什么是工厂模式&#xff1f; 工厂模式&#xff…

【SpringBoot项目】SpringBoot项目-瑞吉外卖【day03】分类管理

文章目录前言公共字段自动填充问题分析代码实现功能测试功能完善新增分类需求分析模型代码开发功能测试分类信息分页查询需求分析代码开发功能测试删除分类需求分析代码开发功能完善修改分类需求分析代码实现结尾&#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315…

11.17 - 每日一题 - 408

每日一句&#xff1a; 世上没有侥幸的成功&#xff0c;只有加倍的努力。 数据结构 1 一棵左右子树均不空的二叉树在先序线索化后&#xff0c;其中空的链域的个数是______ A. 0B. 1C 2D.不确定答案&#xff1a;B 解析&#xff1a;线索二叉树利用了二叉链表中的空的左右孩子指…

高通导航器软件开发包使用指南(3)

高通导航器软件开发包使用指南&#xff08;3&#xff09;3.2 实时数据查看3.3 日志分析3.4 其他日志记录系统信息3.4.1查看数据记录选项3.4.2确保日志存储3.4.3获取snav_vector版本3.2 实时数据查看 snav_sinspector控制台应用程序允许以人工方式查看日志文件中的二进制数据 …

java项目-第142期ssm美食推荐系统-ssm毕业设计_计算机毕业设计

java项目-第142期ssm美食推荐系统-ssm毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm美食推荐系统》 该项目分为2个角色&#xff0c;管理员和用户。 用户可以浏览前台,包含功能有&#xff1a; 首页、热门美食、美食教程、美食店铺 、美食社区、美食资…

Arthas教程

Linux环境安装 下载地址&#xff1a;https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar 运行 quit 退出 stop 停止Arthas快速入门 一.执行一个jar包 二.通过arthas来attach&#xff08;黏附&#xff09; 三.常用命令操作 诊断demo下载http…

Oracle Primavera Unifier活动管理器(Activity Manager)

目录 一、简要介绍 二、其他相关 一、简要介绍 Oracle Primavera Unifier Activity“活动”被定义为必须按计划完成的工作或事件的一部分。 Activity也就是以上的活动&#xff0c;它从映射的 P6 项目中捕获计划数据&#xff0c;从公司级主费率表&#xff08;默认&#xff0…

大数据必学Java基础(一百零二):连接池的使用

文章目录 连接池的使用 一、连接池基础知识扩展 二、代码实战 1、定义连接池

信道划分介质访问控制ALOHA协议CSMA协议CSMA/CD协议轮询访问MAC协议

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录传输数据两种链路点对点链路广播式链路介质访问控制静态划分信道动态划分信道轮询访问介质访问控制随机访问介质访问控制---所有用户都可以随机发送信息ALOHA协议------想说就说CSMA协议------先听再说1-坚持…

【保姆级】新机器部署RabbitMQ

1、登录服务器&#xff0c;如果非root用户则切root用户 sudo su - 2、在/usr/tmp目录上传erlang、rabbitmq安装包 3、将安装包移到/usr/local/目录 mv /usr/tmp/erlang-21.3.8.2-1.el7.x86_64.rpm /usr/local/ mv /usr/tmp/rabbitmq-server-3.7.15-1.el7.noarch.rpm /usr/lo…

基础知识:临界阻尼

任何一个振动系统&#xff0c;当阻尼增加到一定程度时&#xff0c;物体的运动是非周期性的&#xff0c;物体振动连一次都不能完成&#xff0c;只是慢慢地回到平衡位置就停止了。当阻力使振动物体刚好能不作周期性振动而又能最快地回到平衡位置的情况&#xff0c;称为“临界阻尼…

JS高级(三):严格模式、闭包、递归、深拷贝和浅拷贝

JavaScript高级&#xff08;三&#xff09;一、严格模式1.开启严格模式&#xff08;1&#xff09;为脚本开启严格模式&#xff08;2&#xff09;为某个函数开启严格模式2.严格模式的一些规定&#xff08;1&#xff09;禁止变量未声明就赋值&#xff08;2&#xff09;禁止删除已…

AMD发布22.11.1驱动,支持《使命召唤:战区2.0》

他来了他来了&#xff0c;带着迷人的脚步走来了&#xff01; 《使命召唤&#xff1a;战区2.0》正式上线了。有Steam周榜三连冠的《使命召唤19》在前&#xff0c;《战区2.0》可以说是备受瞩目&#xff0c;免费大逃杀&#xff0c;谁不期待&#xff1f; &#xff08;图源自steam&…

一句话生成图片,FlagAI使用(附页面操作代码) | 机器学习

目录 前言 项目结构 页面交互调整 总结 前言 最近Text-To-Image是一个很火的话题&#xff0c;甚至更进一步的Text-To-Video话题度也在不断上升。最近看到一个开源项目FlagAI&#xff0c;是目前我觉着效果比较好的项目之一。安装操作简单&#xff0c;支持中英文&#xff0c;…

疫情防控管理系统

1、项目介绍 疫情防控管理系统拥有两种角色&#xff1a;管理员和用户 管理员&#xff1a;医护信息管理、物资管理、疫苗管理、疫站管理等 用户&#xff1a;登录注册、物资、疫苗、疫站查看 2、项目技术 后端框架&#xff1a; Servlet、mvc模式 前端技术&#xff1a;Bootst…

yolov5剪枝实战1: 论文及yolov5剪枝实战项目介绍

本系列博客介绍yolov5剪枝方法 1. 介绍 神经网络一般都存在过参数化(over-parameterized)的问题,存在冗余的神经元或权重,所以可以进行剪枝。 其实对网络可以针对不同的颗粒度进行剪枝,可以进行权重、神经元级别的剪枝,也可以基于channel, shape,filter以及layer级别的剪枝…

卷积神经网络基础

由于篇幅所限&#xff0c;本章将重点介绍计算机视觉的经典模型&#xff08;卷积神经网络&#xff09;和两个典型任务&#xff08;图像分类和目标检测&#xff09;。主要涵盖如下内容&#xff1a; 卷积神经网络&#xff1a;卷积神经网络&#xff08;Convolutional Neural Netwo…

Nginx知识汇总

一、Nginx的简介 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器&#xff1b;同时也是一个IMAP、POP3、SMTP代理服务器&#xff1b;nginx可以作为一个HTTP服务器进行网站的发布处理&#xff0c;另外nginx可以作为反向代理进行负载均衡的实现。 二、Nginx的优…

基于微信小程序的沁园健身房预约管理系统设计与实现-计算机毕业设计源码+LW文档

小程序开发说明 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Mave…