C++类和对象(下)

news2024/11/19 22:52:09

目录

初始化列表

explicit关键字

Static成员

友元

友元函数

友元类

匿名对象

内部类


初始化列表

初始化列表是以冒号开始,以逗号分割的成员列表,每一个成员变量后面跟一个放在括号中的初始值或表达式。(代码演示以日期类为例)

初始化列表中成员变量括号里是初始值:

class Date
{
public:
	//构造函数
	Date()
		:_year(2001)
		,_month(10)
		,_day(3)
	{
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d2;
	return 0;
}

 初始化列表中成员变量括号里是表达式:

class Date
{
public:
	//构造函数
	Date(int year,int month,int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{

	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d3(2023,1,5);
	return 0;
}

 在来回顾下学习初始化列表前日期类构造函数写法:

class Date
{
public:
	//构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d3(2023, 1, 5);
	return 0;
}

不管是否使用初始化列表,构造函数调用后,对象都会有一个初始值。但是对于未使用初始化列表的写法而言,构造函数函数体中的语句不能称为初始化,而是赋初值!

因为初始化只能一次,所以每个成员变量在初始化列表中只能出现一次。

class Date
{
public:
	//构造函数
	Date(int year,int month,int day)
		: _year(year)
		, _month(month)
		, _day(day)
		, _day(day)
	{ 

	}

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

对于日期类而言,所有的成员变量都是内置类型。对于有自定义成员的场景,会调用它的默认构造。

当自定义类型有默认构造时当然没有问题,但是自定义类型没有默认构造时:

//自定义类型
class A
{
public:
	//构造函数(不是默认构造)
	A(int a)
		:_a(a)
	{
		
	}
private:
	int _a;
};

class B
{
public:
	B(int a,int b)
		:_b(a)
		
	{

	}
private:
	int _b;
	A _c;
};
int main()
{
	return 0;
}

 这种情况如果使用了初始化列表:

//自定义类型
class A
{
public:
	//构造函数(不是默认构造)
	A(int a)
		:_a(a)
	{
		
	}
private:
	int _a;
};

class B
{
public:
	B(int a,int b)
		:_b(a)
		,_c(b)
	{

	}
private:
	int _b;
	A _c;
};
int main()
{
	B b1(1,2);
	return 0;
}

当有默认构造,且使用了初始化列表:

//自定义类型
class A
{
public:
	//默认构造函数
	A(int a = 100)
		:_a(a)
	{
		
	}
private:
	int _a;
};

class B
{
public:
	B(int a,int b)
		:_b(a)
		,_c(b)
	{

	}
private:
	int _b;
	A _c;
};
int main()
{
	B b1(1,2);
	return 0;
}

  小总结:对于自定义类型成员变量,一定会先使用初始化列表初始化。

引用成员变量

引用在变量定义时初始化,在初始化列表位置。

class B
{
public:
	B(int ref)
		
	{}
private:
	int& _ref;
};

int main()
{
	B b3(1);
	return 0;
}

正确写法:

class B
{
public:
	B(int ref)
		:_ref(ref)
	{}
private:
	int& _ref;
};

int main()
{
	B b3(1);
	return 0;
}

 const成员变量

我们知道,const修饰的变量在定义的时候一定要给初值!

int main()
{	
	const int N;
	return 0;
}

在类中const成员变量是声明,所以没有报错。

class A
{

private:
	const int A;
};

对象每个成员什么时候定义初始化呢?

答:初始化列表。

class B
{
public:
	B()
	:_n(10)
	{

	}
private:
	const int _n; 
};
int main()
{
	B bb;
	return 0;
}

成员变量在类中声明次序就是其在初始化列表中的初始化顺序 。

class C
{
public:
	C(int c)
		:_c2(c)
		, _c1(_c2)
	{}

	void Print() {
		cout << _c1 << " " << _c2 << endl;
	}
private:
	int _c1;
	int _c2;
};
int main() 
{
	C c1(2023);
	c1.Print();
	return 0;
}

分析:初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。在上述代码中_c1先声明,所以先初始化,此时_c2是随机值。后初始化_c2。

总结:

●对象的每个成员在初始化列表定义初始化。
●每个成员都要走初始化列表,就算不显示在初始化列表写,也会走。
●如果在初始化列表显示写了就用显示写的初始化。
●如果没有在初始化列表显示初始化:
1、内置类型,有缺省值用缺省值,没有就用随机值。
2、自定义类型,调用默认它的默认构造函数,如果没有默认构造就报错。

explicit关键字

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

int main()
{
    int a = 10;
	double b = a;

	return 0;
}
class Date
{
public:
 Date(int year)
	:_year(year)
{}
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2001);
	// 用一个整形变量给日期类型对象赋值
	// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
	d1 = 2023;
	return 0;
}

explicit 修饰构造函数,将会禁止构造函数的隐式转换
explicit Date(int year)
	:_year(year)
{}

 

Static成员

static用来修饰变量和函数:

■修饰局部变量,更改了局部变量的生命周期,出了作用域依然未被销毁,直到程序结束,生命周期才结束。

■修饰全局变量,使得这个全局变量只能在当前源文件下使用。出了当前文件就没法访问。未使用static修饰,全局变量的作用域是整个工程,其他源文件也可以使用。

■修饰函数,改变函数的的链接属性,只能在当前源文件下使用。

static修饰类中的成员变量和成员函数:

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

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

class A
{
private:
	static int a;
	int b = 1;
	int c = 2;
};
int main()
{
	A aa;
	return 0;
}

2. 静态成员变量必须在类外定义。 

class A
{
public:
	A(int b = 1,int c = 2)
		:_a(1)
	{
		
	}
private:
	static int _a;
	int _b;
	int _c;
};
int main()
{
	A aa;

	return 0;
}

正确定义:
class A
{
public:
	A(int b = 1,int c = 2)
	{
		
	}
private:
	static int _a;
	int _b;
	int _c;
};
int A::_a = 10;
int main()
{
	A aa;
	return 0;
}
3.静态成员也是类的成员,受 访问限定符的限制。
class A
{
public:
	A(int b = 1,int c = 2)
	{
		
	}
private:
	static int _a;
	int _b;
	int _c;
};
int A::_a = 10;
int main()
{
	A aa;
	cout << A::_a;
	cout << aa._a;
	return 0;
}

 

class A
{
public:
	A(int b = 1,int c = 2)
	{
		
	}

	static int _a;
	int _b;
	int _c;
};
int A::_a = 10;
int main()
{
	A aa;
	cout << A::_a<<endl;
	cout << aa._a<<endl;
	return 0;
}

 练习例题:实现一个类,计算程序中创建出了多少个类对象。

class A
{
public:
	A()
	{
		cout << "默认构造" << endl;
		++_N;
	}
private:
	static int _N;
};
int A::_N = 0;
int main()
{
	A a1, a2;
	A a3;
	A a4;
	return 0;
}

 问题:无法访问私有成员,获取_N;

解决方法:写一个静态成员函数,获取_N。

static修饰成员函数,称之为静态成员函数。

​
class A
{
public:
	A() 
	{ 
		cout << "默认构造"<<endl;
		++_N; 
	}
	A(const A & t)
	{ 
		cout << "拷贝构造"<<endl;
		++_N; 
	}
	~A()
	{
		cout << "析构" << endl;
		--_N;
	}
	static int GetACount() 
	{
		return _N; 
	}
private:
	static int _N;
};
int A::_N = 0;
int main()
{
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);
	cout << A::GetACount() << endl;
	return 0;
}

​

调用析构函数之前:

 

调用析构函数后:

题目链接:求1+2+3+...+n_牛客题霸_牛客网

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

class A
{
public:
	A()
	{
		cout << "默认构造" << endl;
		++_N;
	}
	 static int Get_N()
	{
		 _a = 10;
		return _N;
	}
private:
	static int _N;
	int _a;
};
int A::_N = 0;
int main()
{
	A a1, a2;
	return 0;
}

友元

友元函数

场景:在类外定义的函数想要访问类里的成员!此时就可以用友元来解决。(以日期类为例)

class Date
{
public:
	//构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造
	//析构函数
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日";
	}
private:
	int _year;
	int _month;
	int _day;
};

void Fun()
{
	Date d1;
	d1._year = 1314;
	d1.Print();
}

int main()
{
	Fun();
	return 0;
}

此时只用友元解决问题:在类的内部加上类外函数的声明, 声明前加上friend关键字。(友元声明可以在类定义的任何地方声明,不受类访问限定符限制)。

​
class Date
{
	friend void Fun();
public:
	//构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造
	//析构函数
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日";
	}
private:
	int _year;
	int _month;
	int _day;
};

void Fun()
{
	Date d1;
	d1._year = 1314;
	d1.Print();
}

int main()
{
	Fun();
	return 0;
}

​

 例子2:<<和>>重载,不同于其他运算符重载,这两个函数一般写在类外,用友元声明访问类中的成员。因为如果写成类中的成员函数,this指针默认抢占第一个参数的位置,Date对象就是左操作数,我们使用起来就会非常不习惯!

例:

d1>>cout;

 针对这个问题,练习友元的使用。当然在实践之前先对ostream和istream两个类简单的了解下:

 cin是istream类型的对象,cout是ostream类型的对象!简单的说就是库中实现了ostream和istream两个类,cout和cin是实例化出的对象。它们可以很好的处理内置类型的流插入和流提取,这也是在初始c++中谈到的自动识别类型的问题。

但是针对日期类这样的自定义类型是不能搞定的,需要用户显示写。 

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
//流插入重载
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "年" << d._month << "月" << d._day<<"日";
	return _cout;
}
//流提取重载
istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}
int main()
{
	Date d;
	cin >> d;
	cout << d << endl;
	return 0;
}

小总结:

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

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。语法:
class A
{
	friend class B;
private:
	int _a1;
	int _a2;
};
class B
{
public:
	void Fun()
	{
		_aa._a1 = 10;
		_aa._a2 = 100;
	}
private:
	int _b1 = 1;
	int _b2 = 1;
	A _aa;
};

int main()
{
	B bb;
	bb.Fun();
	return 0;
}

需要注意的是,友元的关系是单向的(你把小红当做最好的朋友,在她面前没有秘密,但是从小红的角度看,你只是她最普通不过的一个同学,仅此而已),例如上述代码,A类中有B类的友元声明,所以在B类中可以访问A类的私有成员变量,但是反过来A类在没有声明友元的情况下访问B类的私有成员变量是不可以的! 同时:友元关系是不可传递的,这个也很好理解,小红和小刚是朋友,和小美是朋友,那么小刚和小美一定是朋友吗?答案是否定的。

匿名对象

匿名对象的特点就是不取名字。

//取名
Date d1;
d1.Fun();

//匿名
Date().Fun();

▲匿名对象的声明周期只有一行。

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

调试结果:对象创建的下一行调用析构函数,说明它的声明周期只有一行。

内部类

定义:在一个类中定义了另一个类

class A
{
public:
	//构造
	A()
	{}
	//析构
	~A()
	{}
	class B
	{
	public:
		void Fun()
		{
			A aa;
			aa._a1 = 10;
			aa._a2 = 100;
		}
	private:
		int _b1 = 2;
		int _b2 = 2;
	};
private:
	int _a1 = 1;
	int _a2 = 1;
};

int main()
{
	A::B b;
	b.Fun();
	return 0;
}

内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。

 分析:

★内部类是外部类的友元类,但是外部类不是内部类的友元。

★内部类可以定义在外部类中的任意位置。

★内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名

问题:A的大小是多少。算不算内部类?

class A
{
public:
	class B
	{
	private:
		int _b1 = 2;
		int _b2 = 2;
	};
private:
	int _a1 = 1;
	int _a2 = 1;
};

int main()
{
	A a;
	cout << sizeof(a);
	return 0;
}

 答:sizeof(外部类)=外部类,和内部类没有任何关系。

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

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

相关文章

【实现QQ登录界面 Objective-C语言】

一、实现QQ登录界面 1.实现这样的QQ登录界面 1.实现这样的QQ登录界面 2.首先,分析一下,这个界面里,都有哪些控件 是不是两个Label,两个TextField文本框,1个Button吧 3.先拖1个Label上来 再拖1个TextField文本框上来,在这个Label右边, 然后选中这两个控件, 按住o…

Altium Designer 超详细学习教程——印制电路板基础知识

在介绍Altium Designer软件使用办法之前先介绍下印制电路板的基础知识&#xff0c;不管是画图还是绘制PCB最终目的都是为了制作电路板&#xff0c;因此了解PCB的基础知识很有必要。 1.1印制电路板概述 1.1.1印制电路板结构 在进行PCB设计时&#xff0c;需要先对印制电路板的…

【Linux】对于make/Makefile的使用

本文目录 背景简介 细说关于make命令和makefile文件&#xff1a; 使用方法 为什么执行的指令是make和make clean呢&#xff1f; gcc如何判断文件是否需要重新执行&#xff1f; 背景简介 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&…

064-Tcp/IP通讯使用ServerSocket及Server实现多人在线聊天项目实训

【上一讲】063-Tcp/IP通讯使用ServerSocket及Server实现多人在线聊天_CSDN专家-赖老师(软件之家)的博客-CSDN博客 本文主要讲解使用ServerSocket,Socket类实现多人同时在线聊天的网络通讯程序,主要功能及技术如下: 1.使用ServerSocket,Socket类实现网络通讯功能; 2.使用…

【go语言之websocket】

go语言之websocket写在前面服务端msggetConnUpgradeNewWsConnectionNextWriterwriteread客户端抓包表现总结写在前面 之前的文章都是介绍的是http的使用&#xff0c;这里主要介绍的是websocket,主要是解决长连接场景下的使用。这里概念不多说&#xff0c;网上很多&#xff0c;…

Ajax篇-前后端交互, 接口文档, ajax, axios, fetch,Postman工具

前后端交互接口文档请求方法区分方式GETPOST从语义上区分偏向于查询&#xff08;获取数据....&#xff09;偏向于提交数据(注册,修改,删除...)安全性显示的携带参数,参数是直接拼接在请求地址之后,安全性较差,隐私性差隐式的携带参数,不会在请求地址上显示,安全性好,以JSON格式…

网络虚拟化基本架构

文章目录架构概述架构图核心组件OpenFlow SwitchPipelineFlow Table EntryInstructionOpenFlow Switch Protocol测试验证Pipeline流表项间流程流表间流程Flow Table EntryInstructionsSwitch ProtocolFaucet架构概述 我们知道网络虚拟化的主要目标就是让报文可以在虚拟机之间进…

Windows Anaconda YOLOv3环境部署--2023年1月8日

时效性&#xff1a; 2023年1月8日 目录摘要1 使用 Anaconda 创建虚拟环境2 安装官方要求的依赖库3 验证安装 | 执行 detect.py 示例代码Key already registered with the same priority摘要 网好的可以直接参考官方文档安装&#xff0c;遇到安装报错和网络问题可以参考本文 本地…

网络安全等级保护定级指南 范围

声明 本文是学习github5.com 网站的报告而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 网络安全等级保护 为了配合《中华人民共和国网络安全法》的实施&#xff0c;适应云计算、移动互联、物联网、工业控制和大数据等新技术、新应用情况下网络安全等…

Spring Cloud OpenFeign入门示例

简介 Feign Feign是一个声明性web服务客户端。让编写Web服务客户端变得非常容易&#xff0c;只需创建一个接口并在接口上添加注解即可。让http远程调用就像接口调用一样简单。&#xff08;远程http调用的工具有很多&#xff0c;HttpClient、OKHttp、Spring Boot中的RestTempl…

2023新年红包,兔年HTML红包页面代码【2023新年快乐_附源码】

文章目录一.新年红包&#xff0c;兔年HTML红包页面1.1 资源获取和效果预览二.代码讲解&#xff08;Html文件&#xff09;三.Html文件代码展示&#xff08;需要全部源码请到文章开头链接处下载&#xff09;一.新年红包&#xff0c;兔年HTML红包页面 1.1 资源获取和效果预览 1.…

【Nacos】- Mac-M1下Nacos安装及Nacos启动报错“have ‘x86_64’,need ‘arm64e‘”

Nacos安装及Nacos启动报错“have ‘x86_64’,need arm64e”nacos下载启动nacos问题描述解决方案1、下载安装x86架构的jdk2、更换nacos版本&#xff1a;我这是更换为1.4.2的nacos下载 下载地址&#xff1a;https://github.com/alibaba/nacos/releases 根据自己的工具及需要版本…

LINUX 动态库的版本控制

Linux库文件名的描述版本信息library filename lib <libaray name> .so <libarary version information>库版本信息通常使用以下格式&#xff1a;dynamic libarary version information <M>.<m>.<p>其中&#xff0c;M用一位或多位数字表示库…

(九)汇编语言——转义指令的原理

&#xff08;九&#xff09;汇编语言——转移指令的原理 文章目录&#xff08;九&#xff09;汇编语言——转移指令的原理转移指令作用分类转移行为转移距离转移指令操作符offsetjmp指令功能原理段间转移段内转移短转移原理长转移原理位移越界转移地址寄存器内存段内转移段间转…

【Linux基础】Linux中的时区和时间

基本概念 首先介绍Linux中会用到的时间概念&#xff1a; UTC&#xff1a;Universal Time Coordinated&#xff0c;协调世界时&#xff0c;又称世界统一时间&#xff0c;世界标准时间&#xff0c;国际协调时间。它是一个与时区相关的时间&#xff0c;目前将世界时区分为24个。…

【练习】Day04(未完成版)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、选择二、编程1. 数组中的第K个最大元素2. 组合总数III答案1. 选择2. 编程普通小孩也要热爱生活&#xff01; 一、选择 下面代码运行结果是&#xff08; &#xff09; public class Test{public int add(int a…

传统图像特征描述及提取方法

图像特征描述 图像特征是一幅图像区别于另一幅图像的最基本特征&#xff0c;是其可以作为欸标志性的属性。 图像特征分为两大类: 自然特征&#xff1a;图像本身都具有内在的图像特征&#xff08;如图像的大小、颜色、轮廓、边缘、纹理等&#xff09; 人为特征&#xff1a;便于…

【Linux】基础 IO

文章目录一、文件相关基础知识二、文件操作1、语言层面的文件操作与操作系统层面的文件操作的关系2、C语言文件操作3、操作系统文件操作3.1 比特位传递选项3.2 文件相关系统调用3.3 文件操作接口的使用三、文件描述符1、什么是文件描述符2、文件描述符的分配规则四、重定向1、什…

Docker三剑客——Docker Compose

目录 一、概述 二、Docker Compose工作流程 三、安装Docker Compose 四、Docker Compose管理命令 &#xff08;1&#xff09;docker-compose build &#xff08;2&#xff09;docker-compose kill &#xff08;3&#xff09;docker-compose logs &#xff08;4&#xff…

unity 实现千人同屏

作为开发人员&#xff0c;我们总是关注性能&#xff0c;包括CPU和GPU。随着场景变得越来越大越来越复杂&#xff0c;保持良好的性能变得越来越有挑战性&#xff0c;尤其是当我们添加越来越多的角色时。我和我在上海的同事在帮助客户时经常遇到这个问题&#xff0c;所以我们决定…