C++好难(4):类和对象(下)

news2024/9/29 5:25:25

okk我们终于来到了C++类和对象的最后一节,大多都是对之前学习的内容做的补充

所以加油继续冲啦!

       ∧_∧::
   (´・ω・`)::
  /⌒  ⌒)::
 /へ__  / /::
(_\\  ミ)/::
  | `-イ::
  /  y  )::
 //  /::
/ /::
( く:::
|\ ヽ:::


【本节目标】

  1. 再谈构造函数
  2. Static成员
  3. 友元
  4. 内部类
  5. 匿名对象

目录

【本节目标】

1.再谈构造函数

1.1构造函数体赋值

1.2 初始化列表

初始化列表的特性

2. static 成员

2.1概念

2.2特性

3.友元

3.1友元函数

友元函数的特点:

3.2 友元类

4.内部类

4.1 概念

5.匿名对象


1.再谈构造函数

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;
};

这里需要注意:

上述代码再构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对象成员变量的初始化,

构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

理由如下代码:

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year; // 第一次赋值
		_year = 2022; // 第二次赋值
		//...还可以赋值很多次

		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

这段代码中,构造函数对_year的多次复制,其编译过程是被允许的。

那么既然构造函数里面进行的是赋值的话,什么时候才是成员变量的初始化?

这就要引出我们真正的初始方式:初始化列表

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;
};

int main()
{
	Date d1;// 类对象的整体初始化
//(初始化了类对象,但没有初始化成员变量),成员变量放到了初始化列表里面解决

	return 0;
}

初始化列表是对成员函数的初始化,他直接跟在析构函数参数括号的后面

初始化列表的特性

特性一:
解决一些必须在声明时就要初始化的成员变量

(1)const修饰的成员变量

class Date
{
private:
	const int _year; // const修饰的成员变量只能在定义时初始化
};

const 修饰的变量也必须在定义时就给其一个初始值,也必须使用初始化列表进行初始化。

(2)引用成员变量

class Date
{
private:
	int& _year; // 引用成员变量只能在定义时初始化
};

引用类型的变量在定义时就必须给其一个初始值,所以引用成员变量必须使用初始化列表对其进行初始化。

(3)自定义类型成员(该类没有默认构造函数)

class A 
{
public:
	A(int val) //注:这个不叫默认构造函数(因为需要传参调用)
	{
		_val = val;
	}
private:
	int _val;
};

class B
{
public:
	B()
		:_a(2023) //所以必须使用初始化列表对其进行初始化
	{}
private:
	A _a; //自定义类型成员(该类没有默认构造函数)⬆
};

在初始化列表:_a(2023)也就是对A类的构造函数进行的一次调用

肯定有人会有这样的疑惑:   为什么不直接在类中声明时就给其初始化?

像这样:,但这样的写法,放在现在是可以的,但在C11发布之前是不行的

class A
{
public:
	A()
	{}

private:
	int a;
	const int a1 = 1;
	int& b = a;
};

int main()
{
	A d1;

	return 0;
}

要注意:

在类里面声明时给值,不叫做初始化,而是缺省参数,在C11发布之前不允许在类里面直接定义,所以才会有初始化列表的应用

特性二:
每个成员变量在初始化列表中,只能出现一次,(只能初始化一次)

因为初始化只能进行一次,所以同一个成员变量在初始化列表中不能多次出现

特性三:
在你不写初始化列表时,系统自动生成的初始化列表,对内置类型不处理,对自定义类型也是去调用它的初始化类别,

和构造函数、析构函数的特点很像

因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。

所以,在我们写构造函数的时候,尽量使用初始化列表进行初始化,能提高效率

如下:

// Date类
class Date
{
public:
	Date(int day = 0)
	{
		_day = day;
	}
private:
	int _day;
};

// Test类
class Test
{
public:
	Test(int day)
		:_d(12)//调用Date的构造函数
	{}
	//这样用初始化列表,只用调用一次Date类的构造函数


	Test(int day)
	{ 
		Date t(day);
		_d = t;
	}
	//这样写的话,会调用两次Date类的构造函数
	//因为系统在自动生成初始化列表时就会调用一次,之后在{ }里面又会调用一次

private:
	Date _d;
};

特性四:
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,
与其在初始化列表中的先后次序无关

int i = 0;

class Test
{
public:
	Test()
		:_b(i++)
		, _a(i++)
	{}
	void Print()
	{
		cout << "_a:" << _a << endl;
		cout << "_b:" << _b << endl;
	}
private:
	int _a;
	int _b;
};

int main()
{
	Test test;
	test.Print();
	return 0;
}

结果:

可以看到代码中,在初始化列表里面,我们先初始化的_b,然后是_a,但是结果却不按这个顺序走,因为初始化顺序是按照声明时的顺序走的

2. static 成员

2.1概念

声明为static的类成员称为类的静态成员

static修饰的成员变量,称之为静态成员变量
static修饰的成员函数,称之为静态成员函数

静态成员变量一定要在类外进行初始化

2.2特性

特性一

(1)静态成员所有类对象所共享,不属于某个具体的实例,存放在静态区 

先看下面的代码

我们发现 Test2 的类里面比 Test1 类多定义了一个静态变量 _aaa 但是其空间并没有增加

因为静态成员 _aaa 是存储在静态区的,属于整个类,也属于类的所有对象。所以计算类的大小或是类对象的大小时,静态成员并不计入其总大小之和。

特性二

(2)静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

class Date
{
private:
    //静态成员变量的声明
	static int _year;
	static int _month;
	static int _day;
};

// 静态成员变量的定义初始化
int Date::_year = 2023;
int Date::_month = 5;
int Date::_day = 8;

特性三

(3)类静态成员即可用 类名::静态成员函数 或者 对象.静态成员函数 来访问

class Date
{
public:
	static void Print()
	{
		cout << _year << "-" << _month << endl;
	}
private:
	static int _year;
	static int _month;
};

int Date::_year = 2022;
int Date::_month = 10;

int main()
{
	Date d1;

	d1.Print(); // 对象.静态成员函数

	Date::Print(); // 类名::静态成员函数
	return 0;
}

应为static定义的成员函数是所有类对象所共享的,所以它既可以用类域::来定位,也可以用对象.来定位

特性四

(4)静态成员函数没有隐藏的this指针,不能访问任何非静态成员

class Date
{
public:
	static void Print()
	{
		cout << _year << endl; //静态的成员可以访问
		cout << _month << endl; //不能访问非静态成员
	}
private:
	static int _year;
	int _month;
};

int Date::_year = 2022;

int main()
{
	Date d1;

	d1.Print(); // 对象.静态成员

	return 0;
}

 含有静态成员变量的类,一般含有一个静态成员函数,用于访问静态成员变量。

特性五

(5)静态成员也是类的成员,受public、protected、private 访问限定符的限制

在静态成员变量设为private时,经过我们通过类域进行访问,也是不行的

特性六

 (6)访问静态成员变量的方法:

class Date
{
public:
	static int GetMonth()
	{
		return _month;
	}
	static int _year;
private:
	static int _month;
};

//静态成员变量的定义初始化
int Date::_year = 2023;
int Date::_month = 5;


int main()
{
	Date d1;

	//对公共属性的静态成员进行访问
	cout << d1._year << endl; // 1.通过类对象突破类域进行访问
	cout << Date()._year << endl; // 2.通过匿名对象突破类域进行访问
	cout << Date::_year << endl; // 3.通过类名突破类域进行访问

	//对私有属性的静态成员进行访问
	cout << d1.GetMonth() << endl; // 1.通过类对象突破类域进行访问
	cout << Date().GetMonth() << endl; // 2.通过匿名对象突破类域进行访问
	cout << Date::GetMonth() << endl; // 3.通过类名突破类域进行访问

	return 0;
}

3.友元

友元分为:友元函数友元类

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用

3.1友元函数

问题:现在尝试去重载operator<<,然后会发现没办法将operator<<重载成成员函数。

如下展示:

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

    std::ostream& operator<<(std::ostream& out)
    {
        out << _year << "-" << _month << "-" << _day;
        return out;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d(2017, 12, 24);
    cout << d;

    return 0;
}

因为 cout 的输出流对象和隐含的this指针抢占第一个参数的位置。
this指针默认是第一个参数也就是左操作数了。
但是实际使用中cout需要是第一个形参对象,才能正常使用。
所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员此时就需要友元来解决。operator>>同理。

用友元来解决如下:

class Date
{
	//友元函数
	// 标准流输出 --> printf
	friend std::ostream& operator<<(std::ostream& out, const Date& d); 
	// 标准流插入 --> scanf
	friend std::istream& operator>>(std::istream& in, Date& d); 
public:
	Date(int year = 2023, int month = 5, int day = 8)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

// <<运算符重载---输入
ostream& operator<<(std::ostream& out, const Date& d) 
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}

// >>运算符重载---输出
istream& operator>>(std::istream& in, Date& d) 
{
	in >> d._year >> d._month >> d._day;
	return in;
}

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

注意:其中 cout ostream 类的一个全局对象cin istream 类的一个全局变量
<< 和 >> 运算符的重载函数具有返回值是为了实现连续的输入和输出操作。

友元函数的特点:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

3.2 友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
	Time(int hour = 12, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

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

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	// 内置类型成员
	int _year;
	int _month;
	int _day;

	// 自定义类型成员
	Time _t;
};
  • 友元关系是单向的,不具有交换性。

比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

  • 友元关系不能传递

如果C是B的友元, B是A的友元,则不能说明C时A的友元。

  • 友元关系不能继承

4.内部类

4.1 概念

如果一个类定义在另一个类的内部,这个内部类就叫做内部类

如下:

class A
{
public:
	// 内部类
	class B // B天生就是A的友元,在B里面可以直接访问A的私有成员
	{
	public:
		void Print(const A& a)
		{
			cout << y << endl; // 可以直接访问静态成员
			cout << a.h << endl; // 也可以访问普通成员
		}
	private:
		int _b;
	};

private:
	static int y;
	int h;
};

int A::y = 1;

int main()
{
	A a; // 定义a对象

	A::B bb; // 定义bb对象

	bb.Print(a); // 把a对象传给bb对象,打印
	return 0;
}

上述代码中,我们在A类里面定义了一个B类,B就叫做A的内部类

B天生就是A的友元,B可以访问A的私有,但不能访问B的私有

注意:

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

5.匿名对象

对于一个类 class  A{ };

通过前面的学习,我们知道,在定义类对象的时候不能这样写:A  aa1();

但是我们可以写成这样: A();

匿名对象的特点:

(1)匿名对象的声明周期只有写匿名周期的那一行

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}

private:
	int _a;
};

int main()
{
    A aa1;    

	A();

    return;
}

通过调试我们可以看到,匿名对象在离开他那一行的时候,他的生命周期就结束了

这样写的方式我们就称为匿名对象,那么匿名对象有什么用呢?

看看下面这段代码:

class Solution {
public:
	int Sum_Solution(int n) 
	{
		//...
		return n;
	}
};
int main()
{
	Solution aaa;
	cout << aaa.Sum_Solution(10) << endl;//一般调用

	cout << Solution().Sum_Solution(10) << endl;//匿名对象调用

	return 0;
}

如果我们要用一个类里面的某一个成员函数,我们可能需要真没为其定义一个实例对象 aaa 出来,然后再去调用该类里面的成员函数

比较麻烦,

所以我们可以直接用过匿名对象来进行解决,匿名对象再其调用完成后会直接销毁,不会占用资源

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

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

相关文章

一种轻松且客观介绍大模型方式,避免过度解读:一

这是我关于《一种轻松且客观介绍大模型方式&#xff0c;避免过度解读》第一篇 一、前言 这篇文章旨在为没有计算机科学背景的读者提供一些关于ChatGPT及其类似的人工智能系统&#xff08;如GPT-3、GPT-4、Bing Chat、Bard等&#xff09;如何工作的原理。ChatGPT是一种聊天机器…

【网络安全】mysql数据库提权

数据库提权 数据库提权适用场景前提条件提权步骤如何探查数据库服务是否启动 mysql提权mysql 获取数据库最高权限账号密码方法1.读取网站配置文件2. 读取数据库存储或备份文件3. 脚本暴力破解 利用udf提权udf定义udf提权原理udf常用函数1. 获取mysql版本号2.udf.dll放置到被攻击…

华为P60 Pro灵犀双Wi-Fi流畅通信,让你体验超凡网速

家里装有宽带的朋友&#xff0c;用手机进行无线网络连接时&#xff0c;经常会发现有2.4G和5G两个Wi-Fi信号网络。Wi-Fi信号是通过无线电波传输的&#xff0c;2.4G和5G是两个不同的频段&#xff0c;Wi-Fi信号就在这样的频段内进行传输。 2.4G信号频率低&#xff0c;在空气或障碍…

【机器学习】集成学习(理论)

集成学习&#xff08;理论&#xff09; 目录 一、何为集成学习二、集成学习最简单的模型&#xff1a;投票策略三、弱学习器的组合算法&#xff1a;自助聚合&#xff08;Bagging模型&#xff09;1、数据划分方法&#xff1a;自助法&#xff08;Bootstrap Method&#xff09;2、B…

决策树的介绍

一、介绍 决策树 (decision tree) 是一类常见的机器学习方法。它是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;最后每个叶节点代表一种分类结果。 例如&#xff0c;我们要对"这是好瓜吗?&qu…

3.docker—应用部署MySQL

文章目录 1、mysql部署2、使用Navicat客户端来连接 docker应用部署 docker出现后&#xff0c;这些软件的安装会变得比较简单 1、mysql部署 四步走&#xff1a; 1️⃣搜索mysql镜像 2️⃣拉取mysql镜像 3️⃣创建容器 4️⃣操作 遇到问题&#xff1a; 容器内的网络服务和…

MySQL定时刷新数据

一、步骤 1.查看定时策略是否开启&#xff0c;查看命令: show variables like %event_sche%; 2.显示的 event_scheduler 为 OFF 时用以下命令开启: set global event_scheduler1; 3.创建存储过程 use toursim_platform; -- 选择数据库toursim_platform delimiter // create pro…

【刷题之路Ⅱ】LeetCode 86. 分隔链表

【刷题之路Ⅱ】LeetCode 86. 分隔链表 一、题目描述二、解题1、方法1——先分离再连接1.1、思路分析1.2、代码实现 2、方法2——将较大的节点后移2.1、思路分析2.2、代码实现 一、题目描述 原题连接&#xff1a; 86. 分隔链表 题目描述&#xff1a; 给你一个链表的头节点 head…

科大讯飞交卷,实测星火大模型

作者 | 辰纹 来源 | 洞见新研社 星星之火&#xff0c;可以燎原。 5月6日&#xff0c;讯飞星火认知大模型揭开神秘面纱。 发布会上&#xff0c;科大讯飞董事长刘庆峰、研究院院长刘聪现场实测了星火大模型七大核心能力&#xff0c;并发布基于该大模型的教育、办公、汽车和数字…

docker-mysql的几个问题

来水一篇文章 文章目录 问题一&#xff1a;问题2&#xff1a; 问题一&#xff1a; 在Navicat上执行大脚本mysql的sql文件时&#xff0c;出现插入数据报错的问题&#xff0c;查了一下innodb_log_file_size参数show variables like innodb_log_file_size;只有50331648即48M&…

WB_BF项目问题说明以及探究

我就现在WB_BF项目群里面提到的“根据测试脚本运行日志来看&#xff0c;bf运行了约31小时后又开始出现了api调用返回nginx的错误信息。之后&#xff0c;bf客户端也无法打开。”问题做一下说明&#xff0c;今天早上我在机器上复现了这一问题。针对于api调用会返回nginx的错误信息…

CTF权威指南 笔记 -第四章Linux安全机制-4.1-Linux基础

常用命令 这里给出linux常用命令 cd ls pwd 显示当前工作目录 uname 打印系统信息 whoami 打印用户名 man 查询帮助信息 find echo cat less head grep diff mv cp rm ps top kill touch 创建文件 mkdir 创建文件夹 chmod 变更权限 chown 变更所属者 nano 终端文本编辑器 e…

MySQL获取当前日期、时间、时间戳函数

目录 1.MySQL 获取当前日期时间 函数 1.1 获取当前日期&#xff08;date&#xff09;函数&#xff1a;curdate() 1.2 获取当前时间&#xff08;time&#xff09;函数&#xff1a;curtime() 1.3 获取当前日期时间&#xff08;date time&#xff09;函数&#xff1a;now() …

BClinux8.6 制作openssh9.3p1 rpm升级包和升级实战

一、背景说明 BClinux8.6 默认安装的openssh 版本为8.0&#xff0c;经绿盟扫描&#xff0c;存在高危漏洞&#xff0c;需要升级到最新。 官网只提供编译安装包&#xff0c;而BClinux8.6 为rpm方式安装。 为了方便升级&#xff0c;先通过编译安装包&#xff0c;制作rpm包&…

什么是无感电阻?无感电阻和普通电阻的区别

无感电阻&#xff0c;也称为电感电阻、电感器、电感元件等&#xff0c;是一种电气元件&#xff0c;常用于电子电路中&#xff0c;用于限制电流、防止电磁干扰等。无感电阻是指一种电阻器件&#xff0c;它能够在高频电路中工作而不会产生电感&#xff0c;从而避免了电感对电路性…

【Python】更改matplotlib绘图样式,要创建一个后缀名为mplstyle的样式清单,如何实现?

要更改 matplotlib 绘图样式&#xff0c;可以按照以下步骤创建一个后缀名为 mplstyle 的样式清单&#xff1a; 打开终端或 Anaconda Prompt&#xff08;Windows 用户&#xff09;&#xff1b;确保您的 Matplotlib 版本是 2.0.0 以上版本&#xff0c;通过运行&#xff1a; imp…

被裁现状,给找工作的同学一些建议

2022 到 2023 国内知名互联网公司腾讯、阿里、百度、快手、滴滴、京东、阿里、爱奇艺、知乎、字节跳动、小米等公司均有裁员&#xff0c;其中有不少公司&#xff0c;在过去年的一整年&#xff0c;进行了多轮裁员&#xff0c;以下是网传的一张 “2022 年裁员企业名单”。 这些裁…

【单目标优化算法】孔雀优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

介绍一个empty(空状态描述)全端通用的空状态描述组件

介绍 这是一个全端通用的空状态描述组件&#xff0c;集成了25种常用场景&#xff0c;支持自定义图标及内容&#xff0c;快点下载试试吧。 插件含全部源码&#xff0c;可以给您无限实现可能&#xff0c;随心所欲自定义你的功能&#xff1b;符合uni_modules和easycom规范&#…

什么是web3 | 区块链web3.0人才

文章目录 一、Web31. 什么是web3&#xff1f;2. web3的dapp架构 二、区块链web3.0人才1. 区块链开发技术栈2. 欧易对人才的要求3. 如何成为一名合格的智能合约高级工程师4. web3各个赛道5. 链上数据分析师6. 一些案例 三、参考 一、Web3 1. 什么是web3&#xff1f; 20世纪90年…