C++类与对象(补)

news2024/11/15 19:50:08

 

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步

个人主页:LaNzikinh-CSDN博客

文章目录

  • 前言
  • 一.默认成员函数
  • 二.static
  • 三.友元
  • 四.匿名对象
  • 总结

前言

类的默认成员函数,默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。一个类,我 们不写的情况下编译器会默认生成以下6个默认成员函数。


一.默认成员函数

1.1构造函数

因为前面对构造函数进行过了讲解,这里只做补充

注意:构造函数就是替代了以前C语言中的初始化,对象实例化时系统会自动调用对应的构造函数,构造函数是可以重载的

默认构造函数概念:1.无参构造函数,2.全缺省的,3.不写构造时编译器默认生成的。这三个函数有且只能有一个存在,总结:不传实参数就可以调用的构造叫做默认构造

对于编译器默认生成的构造,对内置类型成员变量初始化没有要求,但对于自定义类型成员变量,会调用这个成员变量的默认构造函数,如果这个成员变量没有构造函数就会报错,这时候就用初始化成员列表。

那么什么是内置类型和自定类型呢?C++把类型分为两类,内置类型和自定义类型,内置类型就是int,char,float,指针这种,自定义类型就是类,结构体,class,struct

这句话不是很好理解,我们来举个例子,方便理解一下

例子:

这段代码运行来是没有任何问题的,但是如果修改就会有问题

#include<iostream>
using namespace std;
class Date
{
public:
	
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2025, 1, 1); 
	Date d3();
	return 0;
}

我们如果把第二个构造函数给注释调的话,那么程序就会出错,因为我第二个对象创造的时候没有构造函数给他调用,所以就会报错

同理,如果我把第一个注释掉的话也会报错,因为第一个和第三个没有对应的构造函数

那如果我把两个构造函数全部注释掉可以吗?系统不是有一个默认的吗?系统会帮我们生成一个没有参数的无参构造函数,那么如果想要程序运行成功的话,就要把第二个对象也注释

那么默认的构造函数到底什么时候用呢?或者是说我什么时候要写构造函数什么时候不写呢?我们之前学过一个用栈来构造队列,如果把它用C++来写的话

class MyQueue
{
public:
	private:
	Stack pushst;
	Stack popst;
};

编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化

如果我的成员对象是有构造函数的时候,他会先调用成员对象的构造函数,这种情况下如果帮我初始化了的话,我就不需要初始化了,我就不需要调用了,总结:大多数情况下,构造函数都需要我们自己来实现,少数情况下不需要

1.2析构函数

析构函数不想做太多的补充,跟之前一样,析构函数对应着就是C语言中的销毁,注意:自定义类型的成员也会调用它的析构,也就是说自定义类型成员无论什么情况都会自动调用他的析构函数。

1.3拷贝构造函数

拷贝构造函数是类与对象中的一个难点,拷贝构造函数是构造函数的一个重载,他有一个特别的地方,就是第一个参数必须是自身类型的引用,不然会引发无穷递归调用的报错

C++语法规定,传值传参会掉用拷贝构造函数。

我们之前讲过引用的本质是常量指针,所以说,如果不是引用的话,他就会无限递归

若未显示定义拷贝构造函数,编译器会自动生成一个拷贝构造函数,自动生成的拷贝构造对内置类型成员变量会完成浅拷贝,对自定义类型的成员变量会调用它的拷贝构造函数

这个地方和前面的有所差距,比如说我们再用那个站来实现队列的例子,这个地方就不能用系统自己的拷贝构造了

int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	// Stack不显示实现拷贝构造,用自动生成的拷贝构造完成浅拷贝
	// 会导致st1和st2里面的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃
	Stack st2 = st1;
	MyQueue mq1;
	// MyQueue自动生成的拷贝构造,会自动调用Stack拷贝构造完成pushst/popst
	// 的拷贝,只要Stack拷贝构造自己实现了深拷贝,他就没问题
	MyQueue mq2 = mq1;
	return 0;
}

这里我们在前面的类与对象中讲了浅拷贝和深拷贝的问题,在这个地方就不多做解释了。

Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}

1.4赋值运算符重载

赋值运算符重载就是把一个对象复制给另一个对象。拷贝能分两种,一种是赋值重载拷贝,一种是拷贝构造,那到底怎么区分呢?赋值重载就是两个都存在的对象进行赋值,而拷贝构造是把初始化的对象去赋给没有初始化的对象。

int main()
{
	Date d1(2024, 7, 5);
	Date d2(2024, 7, 6);
	//赋值重载拷贝
	operator==(d1, d2);
	d1 == d2;
	//拷贝构造
	Date d3(d2);
	Date d4 = d2;
	return 0;
}

C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。运算符重载是具有特名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体。

bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}

.*   ::    sizeof   ?:   . 注意以上5个运算符不能重载

重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。
C++规定,后置++重载时,增加一个int形参,跟前置++构成函数重载,方便区分。

重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第一个个形参位置,第一个形参位置是左侧运算对象,调用时就变成了 对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类类型对象。

这个地方不做过多的讲解,到时候会专门来出一个日期类的实现,就专门运用这个赋值运算符重载来实现的。

1.5取地址运算符重载

将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。 • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this

class Date
{

public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// void Print(const Date* const this) const
	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//这里非const也可以调用const成员函数这是⼀种权限的缩⼩
	Date d1(2024, 7, 5);
	d1.Print();
	const Date d2(2024, 8, 5);
	d2.Print();
	return 0;
}

1.6*取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非⼀些很特殊的场景,逼如我们不想让别人取到当 前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。

class Date
{
public:
	Date* operator&()
	{
		return this;
		// return nullptr;
	}
		const Date * operator&()const
	{
		return this;
		// return nullptr;
	}
private:
	int _year;
	int _month; 
	int _day; 
};

二.static

用static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化,因为不存在对象中,存放在静态区用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针,静态成员也是类的成员,受public、protected、private 访问限定符的限制。

class A
{

static int GetACount()
{
	return _scount;
}
private:
	// 类⾥⾯声明
	static int _scount;
};
// 类外⾯初始化
int A::_scount = 0;
int main()
{

	return 0;

}

例子:求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com)

描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0<𝑛≤2000<n≤200
进阶: 空间复杂度 𝑂(1)O(1) ,时间复杂度 𝑂(𝑛)O(n)

这个题目就和静态成员变量有很大的关系了,如果不用静态成员变量的话,将很复杂,因为这个题目不能用循环这些语句来做,我们可以用C + +的构造函数的特性先创造一个类,专门用来求和,然后在类中初始化两个静态成员变量,一个初始化1,一个初始化0为什么要用到静态成员变量呢?因为它是一个累加的过程,我只有用到静态成员变量才不会出了,函数就销毁,就直接可以一直保存下来。在利用数组反复的调用构造函数完成题目,这里用了变长数组也可以用new来动态开辟。

class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return Sum::GetRet();
}
};

有static成员变量就肯定有static成员函数

1.静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。2.非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。 3.突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员来访问静态成员变量和静态成员函数。

#include<iostream>
using namespace std;
class A
{
public:

	static int GetACount()
	{
		return _scount;
	}
private:
	
	static int _scount;
};
int A::_scount = 0;
int main()
{
	A a1;
	cout << A::GetACount() << endl;
	cout << a1.GetACount() << endl;
	return 0;
}

三.友元

如果我们需要访问内里的私有成员,该如何访问呢?友元提供了⼀种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类 声明的前面加friend,并且把友元声明放到⼀个类的里面。

class A
{
	// 友元声明
	friend void func(const A& aa);
private:
	int _a1 = 1;
};

void func(const A& aa)
{
	cout << aa._a1 << endl;
}
int main()
{
	A aa;
	func(aa);
	return 0;
}

除了友元函数,还有友元类,友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元。

class A
{
	// 友元声明
	friend class B;
private:
	int _a1 = 1;
};
class B
{
public:
	void func1(const A& aa)
	{
		cout << aa._a1 << endl;
		cout << _b1 << endl;
	}
private:
	int _b1 = 3;
};
int main()
{
	A aa;
	B bb;
	bb.func1(aa);
	return 0;
}

内部类

如果⼀个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在 全局相比,他只是受外部类类域限制和访问限定符限制,内部类默认是外部类的友元类

class A
{
private:
	static int _k;
	int _h = 1;
public:
	class B // B默认就是A的友元
	{
	public:
	
			void foo(const A & a)
		{
			cout << _k << endl; 
			cout << a._h << endl; 
		}
	};
};
int A::_k = 1;

A是B的外步类,B默认就是A的友元,B是A的友元,B可以访问到A的私有,A不可以访问B的

四.匿名对象

匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象,可以说明明对象就是一个一次性产品,比如有些时候我只需要用一次,就没有必要再创造一个类出来

class A
{
    private:
		int a = 2;
		int b = 1;
public:
	void print()
	{
		cout << print << endl;
	}
};
int main()
{
	A().print();

	return 0;
}

总结

这次是对之前的类与对象的一个补充,完善了一下类与对象的实现。

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

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

相关文章

充电宝选哪个好?选充电宝主要看什么?充电宝攻略请收下!

当我们的手机、平板等设备电量告急时&#xff0c;充电宝就如同一位救星&#xff0c;为我们解决燃眉之急。然而&#xff0c;面对市场上琳琅满目的充电宝产品&#xff0c;“充电宝选哪个好&#xff1f;”这一问题常常让我们感到困惑。选择一款合适的充电宝并非易事&#xff0c;需…

Qt支持LG高级汽车内容平台

Qt Group与LG 电子&#xff08;简称LG&#xff09;正携手合作&#xff0c;将Qt软件框架嵌入其基于 webOS的ACPLG车载娱乐平台&#xff0c;用于应用程序开发。该合作旨在让原始设备制造商&#xff08;OEM&#xff09;的开发者和设计师能为汽车创建更具创新性的沉浸式汽车内容流媒…

ClickHouse集成LDAP实现简单的用户认证

1.这里我的ldap安装的是docker版的 docker安装的化就yum就好了 sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl start docker 使用下面的命令验证sudo docker run hello-world docker pull osixia/openl…

SQL Server Query Store Settings (查询存储设置)

参考&#xff1a;Query Store Settings - Erin Stellato 在 SQL Server 2017 中&#xff0c;有九 (9) 个设置与查询存储相关。虽然这些设置记录在sys.database_query_store_options中&#xff0c;但我经常被问到每个设置的值“应该”是多少。我在下面列出了每个设置&am…

Puppeteer动态代理实战:提升数据抓取效率

引言 Puppeteer是由Google Chrome团队开发的一个Node.js库&#xff0c;用于控制Chrome或Chromium浏览器。它提供了高级API&#xff0c;可以进行网页自动化操作&#xff0c;包括导航、屏幕截图、生成PDF、捕获网络活动等。在本文中&#xff0c;我们将重点介绍如何使用Puppeteer…

移动打车项目

1.技术栈&#xff1a; AndroidJNIHTTPSlibeventmysql/redis高德地图 2.概要流程设计 1.注册 2.登录 3.司机地理位置上传更新 4.乘客地理位置上传更新 5.乘客下单流程 6.司机完成订单流程

[AWS]EKS启动HPA,HPA指标<unknown>,报错:error: Metrics API not available

背景&#xff1a;在AWS上创建的EKS集群&#xff0c;想要对于deployment部署HPA&#xff0c;来autoscling副本数。 1.HPA一般基于CPU或者内存对副本数进行控制&#xff0c;所以必须需要Metrics Server。 &#xff08;Metrics Server 是 Kubernetes 集群的一个关键组件&#xff0…

7、自定义管理站点

目录 1、自定义后台表单2、添加关联的对象3、自定义后台更改列表4、自定义后台界面和风格&#xff08;1&#xff09;自定义你的工程的模板&#xff08;2&#xff09;自定义你应用的模板 1、自定义后台表单 通过admin.site.register(Question)注册Question模型&#xff0c;Djan…

AI自动生成PPT哪个软件好?高效制作PPT优选这4个

7.15初伏的到来&#xff0c;也宣告三伏天的酷热正式拉开序幕~在这个传统的节气里&#xff0c;人们以各种方式避暑纳凉&#xff0c;享受夏日的悠闲时光。 而除了传统的避暑活动&#xff0c;我们还可以用一种新颖的方式记录和分享这份夏日的清凉——那就是通过PPT的方式将这一传…

抖音视频素材去哪里找啊?视频素材网站库分享

在这个视觉盛宴的抖音平台上&#xff0c;高质量和有趣的视频素材常常是吸引观众的重要钥匙。如果你也正在寻找那些能让你的视频作品更加出色的资源&#xff0c;那么恭喜你&#xff0c;今天我将为你介绍10个超实用的视频素材网站&#xff0c;让你的抖音视频创作充满创意和效率。…

只用 CSS 能玩出什么花样?

在前端开发领域&#xff0c;CSS 不仅仅是一种样式语言&#xff0c;它更像是一位多才多艺的艺术家&#xff0c;能够创造出令人惊叹的视觉效果。本文将带你探索 CSS 的无限可能&#xff0c;从基本形状到动态动画&#xff0c;从几何艺术到仿生设计&#xff0c;只用 CSS 就能玩出令…

linux开机后不用登陆,无法正常进入系统,出现:/#的提示符

linux开机后不用登陆&#xff0c;无法正常进入系统&#xff0c;出现:/#的提示符 解决方案&#xff1a; 1、输入命令 ls /dev/mapper 此时会出现3个文件。其中rhel-root文件 是我们下面所要用的文件。 ls的目的就是为了让大家能知道自己带"-root" 文件的前缀是什…

Linux·权限与工具(上)

1. shell命令以及运行原理 Linux严格意义上来说是一个操作系统&#xff0c;我们称之为 "核心(kernel)" &#xff0c;但我们一般的用户不呢个直接使用到核心&#xff0c;因为不会用。所以开发者在核心外面封装了一层 "外壳(shell)" 程序&#xff0c;来与核心…

某安居客滑块验证码

⚠️前言⚠️ 本文仅用于学术交流。 学习探讨逆向知识&#xff0c;欢迎私信共享学习心得。 如有侵权&#xff0c;联系博主删除。 请勿商用&#xff0c;否则后果自负。 网址 aHR0cHM6Ly93d3cuYW5qdWtlLmNvbS9jYXB0Y2hhLXZlcmlmeS8/Y2FsbGJhY2s9c2hpZWxkJmZyb209YW50aXNwYW0 …

数据包的跨层封装

首先&#xff0c;我们先简单地分析一下数据包的组成结构&#xff1a; 如图 数据包简略地分为以下几层&#xff1a; 二层&#xff1a;封装MAC地址&#xff08;数据链路层&#xff09; 三层&#xff1a;封装IP地址 — 表明源IP和目标IP&#xff0c;主要用于路由器之间的信息转发…

SAPUI5基础知识15 - 理解控件的本质

1. 背景 经过一系列的练习&#xff0c;通过不同的SAPUI5控件&#xff0c;我们完成了对应用程序界面的初步设计&#xff0c;在本篇博客中&#xff0c;让我们一起总结下SAPUI5控件的相关知识点&#xff0c;更深入地理解SAPUI5控件的本质。 通常而言&#xff0c;一个典型UI5应用…

FastAPI 学习之路(五十七)校验是否登陆

在之前的分享的中&#xff0c;产生了token&#xff0c;用于做登录的认证&#xff0c;那么产生了token后&#xff0c;登陆携带了token&#xff0c;我们如何校验呢&#xff1f; 先来梳理下逻辑&#xff1a; 1. 调用登录接口&#xff0c;产生token 2.调用依赖登录的接口&#xff…

1.CATIA:CAA调用Excel接口

生成调用Excel的头文件 参考如下进行excel头文件的生成: 如何使用vs2022通过excel.exe生成VC、C++能够使用的头文件 添加如下的接口: #include "CApplication.h" #include "CWorkbook.h" #include "CWorkbooks.h" #include "CWorkshee…

Linux——文件目录指令、时间日期指令、搜索查找指令、压缩解压指令

目录 文件目录指令 pwd & ls 查看当前工作目录&显示当前目录所有文件及目录 cd 切换目录 mkdir & rmdir 创建&删除目录 touch 创建空文件 cp 拷贝文件/文件夹指令 rm 删除文件或文件夹 mv 移动或重命名文件 cat 查看文件 more指令 less 查看文件内容…

驱动LSM6DS3TR-C实现高效运动检测与数据采集(8)----中断获取FIFO数据并应用MotionFX库解析空间坐标

驱动LSM6DS3TR-C实现高效运动检测与数据采集.8--中断获取FIFO数据并应用MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启LED开启INT中断参考驱动程序中断读取传感器数据主程序演示 概述 本文将探讨如何使用中断机制获取FIFO数据并应用MotionFX库解析空间坐标。Motio…