C++类与对象(下)

类与对象(下)

  • 1.再谈构造函数
    • 1.1构造函数体赋值
    • 1.2初始化列表
    • 1.3explicit关键字
  • 2.static成员
    • 2.1概念
    • 2.2特性
  • 3.有元
    • 3.1有元函数
    • 3.2有元类
  • 4.内部类
    • 4.1概念及特性
  • 5.匿名对象
  • 6.拷贝对象时的一些编译器优化
  • 7. 再次理解类和对象

1.再谈构造函数

1.1构造函数体赋值

创建对象时,编译器会调用构造函数给对象中的成员变量一个合适的初始值。

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

虽然上述构造函数调用时,每个成员变量都有一个初始值了,但是这并不能称为类对象成员的初始化,构造函数体中的语句只能称为赋初值,而不能称为初始化,因为初始化只能初始一次,而构造函数体内可以多次赋值。

那么C++是如何初始化类对象成员的呢?
初始化列表。

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

初始化列表和函数体内赋初值可以混着用。

class Stack
{
public:
	Stack(int capacity = 4)
		:_top(0)
		,_capacity(capacity)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a = nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}

	}
private:
	int* _a;
	int _top;
	int _capacity;
};

【注意】

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    const成员变量
int main()
{
	//const修饰的变量,必须在定义的时候初始化
	const int i;//报错
	//赋值也不行
	i = 1;//报错

	return 0;
}

对象实例化时整体定义,那么对象的每个成员是什么时候定义的呢?
初始化列表

class A
{
public:
	A()
		:_a(1)//定义
	{

	}

private:
	const int _a;//声明
};

注意:

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

自定义类型成员

class B
{
public:
	B(int b)
		:_i(0)
	{}
	
private:
	int _i;
};

class A
{
public:
	A()
		:_a(1)//不对b对象初始化,这里会报错,类B,不存在默认构造函数
		//在类与对象上说过,必须要传参的构造函数,而自己不传参的话,就会报这个错误。
		//加上对b初始化,就没问题了b(10) //ok
	{}

private:
	const int _a;
	B b;
	
};

引用成员变量
引用在初始化的时候也必须初始,不然就不知道是谁的别名

class A
{
public:
	A()
		:_a(1)//ok
		,_b(10) //ok
		,_ret(0)//ok,注意引用初始化给常量,必须加个const
	{

	}

private:
	const int _a;
	B _b;
	const int& _ret;
	
};
  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
   
private:
    int _a2;
    int _a1;
};

int main() 
{
    A aa(1);
    aa.Print();
}

问:
打印_a1,_a2分别是什么?
答:
声明顺序就是初始化顺序。因此_a2先初始化,是随机值,_a1后初始化,是1。

1.尽量使用初始化列表初始化
2.一个类尽量提供默认构造函数(推荐提供全缺省)

1.3explicit关键字

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

int main()
{
	int i = 0;
	//隐式类型转化,并不是把i变成了doule类型,而是中间会创建一个临时变量
	//这个临时变量是double类型,然后给d
	double d = i;

	return 0;
}
class Date
{
public:
	Date(int year)
		:_year(year)
	{}
	//加上explicit下面会报错
	explicit Date(int year)
		:_year(year)
	{}

private:
	int _year;
};

int main()
{
	Date d1(2023);
	Date d2 = 2024;//隐式类型转换
	const Date& d3=2024;

	return 0;
}

在这里插入图片描述
单参构造函数,没有使用explicit修饰,具有类型转换作用,加上explicit,禁止类型转换

class Date
{
public:

	//多参,但后面可以不用传值
	Date(int year,int month=1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	
	//下面报错
	explicit 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(2023);

	Date d4 = 2024;

	return 0;
}

虽然有很多参数,但是后两个可以不用传值,没有使用explicit修饰,具有类型转换作用,加上explicit修饰,禁止类型转换

C++11支持多参数构造函数,隐式类型转换

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 d = { 2023,6,27 };
	return 0;
}

2.static成员

2.1概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化。

假如让实现一个类,计算调用了多少次的构造函数,怎么写?

我们可能会想到使用全局变量

int N = 0;
class A
{
public:
	A()
	{
		N++;
	}
	A(const A& a)
	{
		N++;
	}
};

int main()
{
	A aa1, aa2;
	A aa3(aa1);

	cout << N << endl;
	return 0;
}

还有一种方法静态成员变量

class A
{
public:
	A()
	{
		_N++;
	}
	A(const A& a)
	{
		_N++;
	}
private:
	static int _N;//声明
};

//静态成员变量,必须要在类外进行初始化
//int _N = 0;//这样写_N还是一个全局变量
int A::_N = 0;//必须要指定一下, 声明周期是全局的,作用域受类域限制


int main()
{
	A aa1, aa2;
	A aa3(aa1);

	cout << A::_N << endl;//但这里会报错,因为_N是私有的;
	//如何访问私有成员呢,在类中可以直接访问。再写一个GetN的函数
	return 0;
}
class A
{
public:
	A()
	{
		_N++;
	}
	A(const A& a)
	{
		_N++;
	}
	//报错
	/*int GetN()
	{
		return _N;
	}*/
	
	//static修饰,没有隐藏的this指针了,只能访问静态成员变量
	static int GetN()
	{
		return _N;
	}
		
private:
	static int _N;//声明
};

int A::_N = 0;


int main()
{
	A aa1, aa2;
	A aa3(aa1);


	//cout << A::GetN() << endl;//不加static,还是会报错,因为GetN不是静态成员函数,不能调用静态成员变量
	cout << A::GetN() << endl;//ok

	return 0;
}

注意,以前我们调用一个类的成员函数,需要创建一个对象,然后才能调用,而静态的成员函数,不用创建对象了,直接就可以访问类的成员函数。

2.2特性

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

【问题】

  1. 静态成员函数可以调用非静态成员函数吗?
  2. 非静态成员函数可以调用类的静态成员函数吗?

【答】
1.不可以
2.可以

3.有元

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

3.1有元函数

在类与对象(中),详细讲了有元函数,这里不再细说。

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

【说明】

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

3.2有元类

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

class Time
{
	friend class Date;//声明Date是Time的有元
public:
	Time(int hour = 0)
		:_hour(hour)
	{}

private:
	int _hour;
};


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

	void SetTime()
	{
		_T._hour = 12;//想要访问另一个类的私有成员,就把自己伪装成它的朋友(声明一下)
	}

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

1.友元关系是单向的,不具有交换性。

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

2.友元关系不能传递

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

3.友元关系不能继承。

4.内部类

4.1概念及特性

概念:如果一个类定义在另一个类的内部这个内部类就叫做内部类内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

class A
{
public:
	class B
	{
	private:
		int _a;
	};
private:
	static int _n;
	int _i;
};

int A::_n=0;


int main()
{
	//计算A类大小
	cout << sizeof(A) << endl;//结果是8,为什么不是12呢?
	//因为,B与A是两个独立的类,把B定义在A类内与定义在A类外是一样的。
	return 0;
}

注意:内部类和外部类是两个独立的类,但是内部类的访问受外部类的类域与访问限定符的限制。

class A
{
public:
	class B
	{
	private:
		int _a;
	};
private:
	static int _n;
	int _i;
};

int main()
{
	B bb;//报错
	A::B bb;//ok
	//如果把B放在私有,上面都会报错
	return 0;
}
class A
{
public:
	class B //B天生就是A的有元,所以B天生就能访问A的私有成员,但A不能访问B
	{
	public:
		void visitA(const A& a)
		{
			cout << a._i << endl;
			cout << _n << endl;
		}
	};
private:
	static int _n;
	int _i;
};

int A::_n=0;

int main()
{
	A::B bb;
	bb.visitA(A());//A()是匿名对象
	return 0;
}

5.匿名对象

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

	A(const A& aa)
	{
		cout << "A(const A& aa)" << endl;
	}

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

int main()
{
	//有名对象
	A a1;
	A a2(a1);
	A a3 = 10;
	//A a4();//不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义

	//但是可以这样定义匿名对象,匿名对象特点不用取名字
	A();
	A(3);

	return 0;
}

注意:
匿名对象也会调用构造函数,生命周期只有当前这一行,走过这一行就会调用析构函数

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}

	void Print()
	{
		cout << _a << endl;
	}


private:
	int _a;
};

int main()
{
	//假如我想打印A,以前方法就是创建一个对象,然后调用Print函数
	A aa;
	aa.Print();

	//现在有了匿名对象,就可以简单一点
	A().Print();

	return 0;
}

6.拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

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

private:
	int _a;
};

void f1(A aa)
{}

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

A f3()
{
	//A aa;
	//return aa;
	return A(10);
}

int main()
{
	//1.
	A aa1 = 1; //本来 构造+拷贝构造  -->优化 构造

	//2.
	A aa2(1);
	f1(aa2); //构造+拷贝构造

	f1(A(1));
	f1(1); //优化 -->构造

	//3.传值返回
	f2();
	A ret = f2();
	
	//4.传值返回
	A ret = f3();
}

在这里插入图片描述

在这里插入图片描述

这里是编译器对传值和传返回值做的一些优化,我们可以学习这样的写法。

7. 再次理解类和对象

现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:

  1. 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
  2. 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
  3. 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象此时计算机才能洗衣机是什么东西。
  4. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。

在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象。

自此类域对象全部学完了,希望每个人都有自己的收获。喜欢的可以支持一下作者,点赞,评论,收藏。咱们下篇文章再见!

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

oracle 重复启动监听程序故障

又是一起 oracle 无法连接问题&#xff0c;检查配置都是正常的。 原来是碰到一个oralce的bugl了。 还真就是这个问题&#xff0c;子进程一kill掉&#xff0c;就恢复了。

【Spring Clound】Nacos高可用集群搭建与使用

文章目录 一、Nacos 简介二、Nacos 安装2.1、Nacos 环境依赖2.2、Nacos 服务端安装 三、Nacos 部署3.1、单实例部署3.2、 集群部署3.2.1、集群架构3.2.2、模拟部署 四、微服务集成Nacos4.1、依赖组件版本选型4.2、注册中心4.2.1、服务提供者4.2.2、服务消费者4.2.3、服务调用4.…

目标检测模型中的Bells and wisthles

目标检测模型中的Bells and wisthles 目标检测模型中的Bells and wisthles1. Data augmentation 数据增强2. Multi-scale Training/Testing 多尺度训练/测试3. Global Context 全局语境4. Box Refinement/Voting 预测框微调/投票法5. OHEM 在线难例挖掘6. Soft NMS 软化非极大抑…

二.《UE4奥丁》解密哈希ID

哈希表概念 1.相信大家经常在UE4或者UE5游戏逆向中遇到下面的代码段 $ > > 41:8B42 0C > mov eax,dword ptr ds:[r10C] > $4 > 3B05 AE589B04 > cmp eax,dword ptr ds:[7FF7B68B74F4] …

观察级水下机器人使用系列之三黑白和彩色摄像机

本文主要讲Valor配套的黑白和彩色摄像机&#xff0c;它们都是imenco公司生产的&#xff0c;黑白照相机型号是Night Shark&#xff0c;彩色照相机型号是Blacktip SharkII&#xff0c;令人奇怪的是黑白照相机比彩色照相机大多了&#xff0c;见下图大的照相机是黑白照相机。 正在上…

Audio API 实现音频播放器

市面上实现音频播放器的库有很多&#xff0c;比如wavesurfer.js、howler.js等等&#xff0c;但是都不支持大音频文件处理&#xff0c;100多M的文件就有可能导致程序崩溃。总之和我目前的需求不太符合&#xff0c;所以打算自己实现一个音频播放器&#xff0c;这样不管什么需求 在…

NLP入门:word2vec self-attention transformer diffusion的技术演变

这一段时间大模型的相关进展如火如荼&#xff0c;吸引了很多人的目光&#xff1b;本文从nlp领域入门的角度来总结相关的技术路线演变路线。 1、introduction 自然语言处理&#xff08;Natural Language Processing&#xff09;&#xff0c;简称NLP&#xff0c;是通过统计学、…

使用Docker Swarm部署PXC+HAProxy高可用集群(三节点)

使用Docker Swarm部署PXCHAProxy高可用集群&#xff08;三节点&#xff09; 1. 部署规划 当前规划中&#xff0c;只启动一个HAProxy服务&#xff0c;主要用来做MySQL节点的负载均衡和代理&#xff0c;但是HAProxy可能会出现单点故障&#xff0c;后续需要启动多个HAProxy节点&…

【网络知识面试】初识协议栈和套接字及连接阶段的三次握手

接上一篇&#xff1a;【网络面试必问】浏览器如何委托协议栈完成消息的收发 1. 协议栈 一直对操作系统系统的内核协议栈理解的模模糊糊&#xff0c;借着这一篇博客做一下简单梳理。 我觉得最直白的理解&#xff0c;内核协议栈就是操作系统中的一个网络控制软件&#xff0c;就是…

Web测试的主要内容和测试方法有哪些?

Web测试的主要内容&#xff1a; 一、输入框 二、搜索功能 三、增加、修改功能 四、删除功能 五、注册、登录模块 六、上传图片测试 七、查询结果列表 八、返回键检查 九、回车键检查 十、刷新键检查 Web测试的测试方法&#xff1a; 1.在测试时&#xff0c;与网络有关的步骤或者…

mybatis-plus在实际开发中的应用

文章目录 前言一、实体类的注解二、Req查询条件三、Controller接口四、Service接口五、Service接口实现类六、Mapper接口七、枚举的使用总结 前言 最近的项目是使用mybatis-plus作为持久层框架&#xff0c;前面也记录过mybatis-plus的基本使用&#xff0c;此次记录一下本次项目…

蓝桥杯专题-试题版-【01字符串】【2n皇后问题】【A+B问题】【Fibonacci数列】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

Python:pyecharts可视化

文章目录 简介Geo地理图绘制地图下载 折线图区域突出显示横坐标带选择展示 add地图Mapformatter控制value显示在图中显示value值目标html的解析自定义地图js资源原生地图js的解析解决省份上文字不居中的问题 桑基图设置桑基柱的颜色 参考文献 简介 &#xff08;这是20年的笔记…

【MySQL】库的操作

目录 一、创建数据库 二、字符集和校验规则 1、查看系统默认字符集以及校验规则 2、创建数据库案例 3、校验规则对数据库的影响 3.1、不区分大小写 3.2、区分大小写 3.3、进行查询 3.3.1、不区分大小写的查询以及结果 3.3.2、区分大小写的查询以及结果 3.4、结果排序…

flutter - 编写 阿里云-金融级实名认证插件

项目中有实名认证的需求&#xff0c;用户上传身份证反正面&#xff0c;进行人脸核验&#xff0c;后台集成的是阿里云的金融级实名认证SDK&#xff0c;巧合的是阿里云没有packages 需要自己造轮子。 废话不多少&#xff0c;直接上代码&#xff1a; 新建项目 ProjectType Plugin…

wsl下面的子系统启用systemctl

下载地址 https://github.com/gdraheim/docker-systemctl-replacement 操作 mv /usr/bin/systemctl /usr/bin/systemctl.old #对原文件进行备份sudo scp /mnt/c/Users/Administrator/Desktop/systemctl.py /usr/bin/systemctl #把项目中的systemctl.py文件拷贝到/use/bin/ 目…

Diffusion扩散模型学习2——Stable Diffusion结构解析-以文本生成图像为例

Diffusion扩散模型学习2——Stable Diffusion结构解析 学习前言源码下载地址网络构建一、什么是Stable Diffusion&#xff08;SD&#xff09;二、Stable Diffusion的组成三、生成流程1、文本编码2、采样流程a、生成初始噪声b、对噪声进行N次采样c、单次采样解析I、预测噪声II、…

安装mmdetection2.22(windows下)

安装mmdetection2.22 确定版本安装mmcv1.4安装mmdetection测试方案1方案2 确定版本 安装mmcv1.4 首先.cuda,pytorch得安装好&#xff0c;这里我拷贝pt1.8虚拟环境 安装mmcv1.4 安装mmdetection 参考文章 下载 cd E:\Code\mmdetection\mmdetection-2.22.0 pip install -r…

MATLAB 之 低层绘图操作和光照及材质处理

这里写目录标题 一、低层绘图操作1. 曲线对象2. 曲面对象3. 文本对象4. 其他核心对象4.1 区域块对象4.2 方框对象 二、光照和材质处理1. 光照处理2. 材质处理2.1 图形对象的反射特性2.2 material 函数 一、低层绘图操作 MATLAB 将曲线、曲面、文本等图形均视为对象&#xff0c…

【MySQL数据库 | 第十九篇】SQL性能分析工具

目录 前言&#xff1a; SQL执行频率&#xff1a; 慢查询日志&#xff1a; profile&#xff1a; profile各个指令&#xff1a; 总结&#xff1a; 前言&#xff1a; 本篇我们将为大家讲解SQL性能的分析工具&#xff0c;而只有熟练的掌握了性能分析的工具&#xff0c;才可以更…