C++类与对象(下)

news2024/11/18 23:28:41

类与对象(下)

  • 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. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。

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

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

在这里插入图片描述

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

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

相关文章

Acer宏碁掠夺者Predator战斧300 PH315-53原厂Windows10系统工厂模式,恢复安装原装OEM预装系统

Acer宏基笔记本电脑&#xff0c;Acer宏碁Predator掠夺者战斧300 PH315-53原装出厂Windows10系统 系统自带所有驱动、Office办公软件、出厂主题壁纸LOGO、 Acer Care Center、Quick Access、PredatorSense风扇键盘背光控制中心等预装程序 所需要工具&#xff1a;32G或以上的U盘…

Django框架实现简单的接口开发

前提创建一个Django项目&#xff0c;目录如下&#xff1a; Django框架上进行GET请求接口开发示例: 1.在上面项目结构目录Template下&#xff0c;新建一个login.html页面&#xff0c;定义表单提交请求的方式为post&#xff0c;具体代码如下。 <!DOCTYPE HTML> <html …

LSTM

其中一个门用来从记忆单元中输出条目&#xff0c;将其称为输出门&#xff08;output gate&#xff09;&#xff1b;另外一个门用来决定何时将数据读入记忆单元&#xff0c;将其称为输入门&#xff08;input gate&#xff09;&#xff1b;同时还需要一种机制来重置单元的内容&am…

Android Jetpack Compose之RadioGroup的使用

Android Jetpack Compose是一个现代化的UI工具包&#xff0c;帮助开发者以声明式的方式构建出美观且功能强大的Android应用。在本文中&#xff0c;我们将详细介绍其中的一个重要组件——RadioGroup。 一. RadioGroup简介 Jetpack Compose中并没有像传统View系统中那样直接提供…

刷题遇到的问题

前言&#xff1a;好记性不如烂笔头&#xff0c;在刷题的时候遇到了如下代码&#xff0c;最终运行结果与我想的答案有所不同&#xff0c;在此记录一下方便下次理解 1、变量提升与函数声明 var time new Date(); function fx() {console.log(time); // undefinedif (false) {…

LeetCode 剑指 Offer 13. 机器人的运动范围(深度遍历)

LeetCode 剑指 Offer 13. 机器人的运动范围 原题思路代码运行截图收获 原题 LeetCode 剑指 Offer 13. 机器人的运动范围 思路 通过深度遍历来找出所有可达的格子通过0、1、2来区分未遍历、可到达、不可到达三种状态 代码 class Solution { public:int visited[109][109];i…

Scala面向对象【下】

1、特质 Scala 语言中&#xff0c;采用特质 trait&#xff08;特征&#xff09;来代替接口的概念&#xff0c;也就是说&#xff0c;多个类具有相同的特质&#xff08;特征&#xff09;时&#xff0c;就可以将这个特质&#xff08;特征&#xff09;独立出来&#xff0c;采用关键…

VSCode 关闭未修改文件编辑器的替换

文章目录 1 关闭编辑预览参考 1 关闭编辑预览 在setting中搜索preview取消enable Preview 选项[可选] 设置不同工作区 参考 vs code取消打开一个文件会替换之前未修改文件 https://blog.csdn.net/networkhunter/article/details/105043771

多个域名映射一个nginx多个80端口

阿里云多个二级域名&#xff0c;解析到同一个机器外网ip地址&#xff1a; http://demo.xxx.com.cn/ http://yang.xxx.com.cn/ nginx.conf 配置文件&#xff1a; server{listen 80;server_name yang.xxx.com.cn;# 第1个二级域名映射80端口index index.html index.htm index.ph…

【深度学习】6-2 卷积神经网络 - 池化层

池化是缩小高、长方向上的空间的运算。比如下图&#xff0c;将2 x 2的区域集约成1个元素的处理&#xff0c;缩小空间大小 上面的例子是按步幅2进行2x2的Max池化时的处理顺序。“Max池化”是获取最大值的运算&#xff0c;“2 x 2”表示目标区域的大小。从2x2的区域中取出最大的…

探索Gradio的Chatbot模块:创建交互式聊天机器人

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

构建高效学生志愿者活动管理系统:基于前后端分离的设计与实现

本文介绍了一种基于前后端分离的学生志愿者活动管理系统的设计与实现。通过前后端分离的架构,系统实现了高度可扩展性和灵活性,能够有效管理学生志愿者活动,并提供友好的用户界面。文章将详细介绍系统的架构设计、技术选型以及核心功能的实现,并给出相应的代码示例。 学生…

Linux——4linux实用操作

目录 4.1 各类小技巧&#xff08;快捷键&#xff09; 4.2 软件安装 4.3 systemctl 4.4 软连接 4.5 日期、时区 4.6 IP地址、主机名 IP地址 主机名 域名解析 配置主机映射 虚拟机配置固定IP 4.7 网络传输 下载和网络请求 端口 4.8 进程管理 4.9 主机状态 4.10 …

使用docx4j实现word转pdf

前言 word文件转pdf docx4j 将word转pdf 导入依赖 <dependency><groupId>org.docx4j</groupId><artifactId>docx4j-JAXB-Internal</artifactId><version>8.3.9</version></dependency><dependency><groupId>or…

计算机网络 - 第一章(下)

1.2_1 分层结构、协议、接口、服务_哔哩哔哩_bilibili1.2_1 分层结构、协议、接口、服务是王道计算机考研 计算机网络的第7集视频&#xff0c;该合集共计76集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https://www.bilibili.com/video/BV19E411D78…

移远通信推出新款卫星通信模组CC660D-LS,加速IoT终端直连卫星

上海&#xff0c;2023年6月27日 — 在2023上海世界移动通信大会&#xff08;MWC Shanghai&#xff09;期间&#xff0c;全球领先的物联网整体解决方案供应商移远通信宣布&#xff0c;推出其在卫星通信领域的最新力作—— CC660D-LS 模组。该模组现阶段面向北美和欧洲市场&#…

【雕爷学编程】Arduino动手做(133)---LCD1602扩展板模块

7款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&…

FDTD Solutions基础到精通,适用于微纳光学领域光学器件、超表面的仿真

专题二&#xff1a; “ FDTD 时域有限差分数值模拟方法与应用 课 程 内 容 FDTD基础入门 1 FDTD Solutions 求解物理问题的方法 1.1 FDTD与麦克斯韦方程 1.2 FDTD中的网格化 2 FDTD Solutions 特点与应用 3 FDTD功能与使用 主窗口——CAD人机交互界面计算机辅助设计…

SQL Server Management Studio (SSMS) 指定端口Port连接, 用逗号, 例如: localhost,1433

Microsoft SQL Server Management Studio (SSMS) 指定端口连接, 用,逗号, 例如 localhost,1433 localhost 等效 localhost,(逗号&#xff09;1433

使用项目跟踪工具,让项目管理更高效,使用项目管理工具的好处

为了确保项目按照预期规划完成&#xff0c;项目经理必须跟踪每个活动的进度。 项目跟踪是贯穿整个项目生命周期的重要活动&#xff0c;它可以通过有效的方式清晰地了解项目的实际进展情况。 项目管理工具可以帮助项目管理者实时跟踪项目状态&#xff0c;及时发现问题并跟踪解…