C++之类和对象(3)

目录

1. 再谈构造函数

1.1 构造函数体赋值

 1.2 初始化列表

1.3 explicit 

 2. static成员

2.1 概念

 3. 友元

3.1 友元函数

3.2 友元类

4. 内部类

 5. 匿名对象

6. 拷贝对象时编译器做出的优化


1. 再谈构造函数

1.1 构造函数体赋值

class Date
{
public:
    Date(int year=2024, int month=3, int day=16)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void pri() {
        cout << _year <<" "<< _month <<" "<< _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main() {
    Date a;
    Date b(111);
    a.pri();
    b.pri();
    return 0;
}

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

 

 1.2 初始化列表

class Date
{
public:
	Date(int year, int month, int day)
        //初始化列表
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	void pri() {
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main() {
	Date a(1,2,3);
	a.pri();
	return 0;
}


初始化列表是每个成员变量定义初始化的位置

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

const成员变量无法在函数体初始化,得在初始化列表初始化

int& ref;引用也得在初始化列表,因为引用定义必须初始化

自定义类型成员(且该类没有默认构造函数时)也在初始化列表



 

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

最后输出 1   -858993460

因为先初始化a2因为a2先在类中声明而且用的是a1的值初始化而a1是随机值

a1最后初始化为a,是1

能用初始化列表就用初始化列表

成员变量的顺序与初始化列表的顺序最好一致,因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关

1.3 explicit 

单参数类型: 

class A {
public:
	A(int a=0) 
	:_aa(a)
	{
		cout <<_aa << endl;
	}
	
private:
	int _aa;
};
class B {
private:
	int _a;
	int* p = nullptr;
	int* pp = (int*)malloc(4);
};
int main() {
	B bb;
	A c1(1);
	A c2 = 2;//单参数构造函数支持隐式类型转换
	//所以是2构造一个临时对象然后拷贝构造
	const A& c3 = 4;
	return 0;
}

缺省值可以是其他的变量不局限于常量
如果不想存在隐式类型转换的话可以加explicit在构造函数前面:

c2和c3就出错了

 多参数类型:

class A {
public:
	 A(int a=0,int b=1) 
	:_aa(a)
    ,_bb(b)
	{
		cout <<_aa <<" ";
		cout << _bb << endl;
	}
	
private:
	int _aa;
	int _bb;
};
class B {
private:
	int _a;
	int* p = nullptr;
	int* pp = (int*)malloc(4);
};
int main() {
	B bb;
	A c1(1,2);
	A c2 = {3,4};
	const A& c3 = {5,6};
	return 0;
}
多参数支持花括号产生隐式类型转换

同样加了explicit就不行了:

 用explicit修饰构造函数,将会禁止构造函数的隐式转换

 2. static成员

2.1 概念

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

静态成员函数没有this指针所以他只是为了访问静态成员变量他访问不了非静态成员变量因为没this

class A
{
public:
    A() {
        ++n;
    }
    A(const A& aa) {
        ++n;
    }
//private:
    static int n;//声明
    //属于整个类,但本质还是静态全局变量
};
int A::n = 0;//在类外定义
void func() {
    n += 1;//那么这里就访问不了n了
}
int main() {
    A a;
    A b;
    cout << n << endl;//这里也是访问不了n
    return 0;

}

 

class A
{
	
	public:
		A() { ++_scount; }
		A(const A & t) { ++_scount; }
		~A() { --_scount; }
		static int GetACount() { return _scount; }
	private:
		static int _scount;
	};
	int A::_scount = 0;
	void TestA()
	{
		cout << A::GetACount() << endl;
		A a1, a2;
		A a3(a1);
		cout << A::GetACount() << endl;
	}
	int main() {
		TestA();
		return 0;
	}

最终结果0 3

当创建对象 a1 时会调用默认构造函数 A()

输出 _scount 的初始值,初始值为 0。

创建对象 a1,_scount 自增为 1。

创建对象 a2,_scount 再次自增为 2。

通过复制构造函数创建对象 a3,_scount 再次自增为 3。

输出 _scount 的最终值,值为 3。

注意:

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

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

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

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

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

 3. 友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元破坏了封装,所以友元不宜多用-慎用

3.1 友元函数

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

class Date
{
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
		
public:
	Date(int year = 1900, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
 
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1;
	cin >> d1;
	cout << d1;
	return 0;
}

友元函数是普通的全局函数:

#include <iostream>
 
using namespace std;
 
class A
{
 
 private:
     int A;
  public:
     print(){};
     
     //声明全局函数 person 是 类A 的友元函数
     friend void person (int &x);
}
 
 
void person(int &x)
{
   //使用了类A的成员变量age
   cout << "age=" << p.age << endl;
  
}
 
int main ()
{
   A p(22);
   person(p);
   return 0;
}

友元函数是其他类的成员函数:

#include <iostream>
 
using namespace std;
 
class A
{
 
 private:
     int A;
  public:
     print(){};
     
     //声明类B的成员函数 person 是 类A 的友元函数
     friend  void B::person (int &x);
}
 
 
class B
{
  private:
     int B;
  public:
     person(int &x);
 
}
 
B::person(int &x)
{
   //因为类B的成员函数person是类A的友元函数,所以看可以使用类A的成员变量age
   cout << "age=" << p.age << endl;
  
}
 
int main ()
{
   A p(22);
 
   B q;
   q.person(p);
   return 0;
  
}

注意:

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

3.2 友元类

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

友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递:如果C是B的友元, B是A的友元,则不能说明C时A的友元。
友元关系不能继承(后续解析)

//时间类
class Time
{
   friend class Date;   
// 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
 
public:
 
 //成员函数的初始化列表
 Time(int hour = 0, 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 = 1900, 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;
};

优缺点总结 :

点:友元函数不是类的成员但是却具有成员的权限,可以访问类中受保护的成员,这破坏了类的封装特性和权限管控;

优点:可以实现类之间的数据共享;比如上面互为友元类,则可以互相访问对方受保护的成员;

总结:友元函数是一种破坏封装特性的机制,可以让程序员写代码更灵活,但是不能滥用

4. 内部类

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

注意:内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

特性:

  • 1. 内部类可以定义在外部类的public、protected、private都是可以的。
  • 2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • 3. sizeof(外部类)=外部类,和内部类没有任何关系。
    // 1、B类受A类域和访问限定符的限制,其实他们是两个独立的类
    // 2、内部类默认就是外部类的友元类
    class A
    {//外部类不能访问内部类
    public:
    	class B // B天生就是A的友元
    	{
    	public:
    		void print(const A& a)
    		{
    			cout << k << endl;   //可以直接访问A的静态成员变量
    			cout << a.h << endl; //也可以访问A的成员变量
    		}
    	};
    private:
    	static int k;
    	int h;
    };
    int A::k = 1;
     
    int main()
    {
    	A::B b;
    	b.print(A());
    	cout << sizeof(A) << endl;  //8   外部类的大小与内部类无关  a与b相互独立的只不过b受a类的局域限制
    	return 0;
    }
    类本身不占用空间

C++不太喜欢使用内部类,所以了解即可 

 5. 匿名对象

匿名对象的特点就是不用取名字,生命周期只存在定义的这一行。

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 aa1();// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	A();// 但是他的生命周期只有这一行,紧接着它的下一步就会自动调用析构函数
 
	A aa2(2);
	return 0;
}

 应用场景: 当我们做C++的OJ题时会发现都是将其封装在一个Solution类中的,假设我们需要调用这个类中的某一个函数,是需要先创建一个Solution的对象,然后通过这个对象进行调用,这样的话有点麻烦,我们可以直接使用匿名对象来调用这个类中的成员函数。

class Solution {
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};
int main()
{
	// 1.基本方法
	Solution sl;
	sl.Sum_Solution(10);
 
	// 2.匿名对象
	Solution().Sum_Solution(10);
 
	return 0;
}

6. 拷贝对象时编译器做出的优化

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

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
void f1(A aa)
{}
A f2()
{
	A aa;
	return aa;
}
int main()
{
	// 传值传参
	A aa1;
	f1(aa1);
	cout << endl;
 
	// 传值返回
	f2();
	cout << endl;
 
	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);
 
	// 一个表达式中,连续构造+拷贝构造->优化为一个构造
	f1(A(2));
	cout << endl;
 
	// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
	A aa2 = f2();
	cout << endl;
 
	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;
	return 0;
}


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

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

相关文章

【Miniconda】基于conda避免运行多个PyTorch项目时发生版本冲突

【Miniconda】基于conda避免运行多个PyTorch项目时发生版本冲突 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到…

Day39:安全开发-JavaEE应用SpringBoot框架Actuator监控泄漏Swagger自动化

目录 SpringBoot-监控系统-Actuator SpringBoot-接口系统-Swagger 思维导图 Java知识点&#xff1a; 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;序列化数据&#xff0c;身份验证&#xff0c;框架开发&#xff0c;第三方组件使用等. 框架库&#xff1a;MyB…

十五、自回归(AutoRegressive)和自编码(AutoEncoding)语言模型

参考自回归语言模型&#xff08;AR&#xff09;和自编码语言模型&#xff08;AE&#xff09; 1 自回归语言模型&#xff08; AR&#xff09; 自回归语言模型&#xff08;AR&#xff09;就是根据上文内容&#xff08;或下文内容&#xff09;预测下一个&#xff08;或前一个&…

由浅到深认识C语言(13):共用体

该文章Github地址&#xff1a;https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.csdn…

Unity Live Capture 中实现面部捕捉同步模型动画

Unity Face Capture 是一个强大的工具&#xff0c;可以帮助你快速轻松地将真实人脸表情捕捉到数字模型中。在本文中&#xff0c;我们将介绍如何在 Unity Face Capture 中实现面部捕捉同步模型动画。 安装 |实时捕获 |4.0.0 (unity3d.com) 安装软件插件 安装 Live Capture 软件…

(一)Neo4j下载安装以及初次使用

&#xff08;一&#xff09;下载 官网地址&#xff1a;Neo4j Graph Database & AnamConnect data as its stored with Neo4j. Perform powerful, complex queries at scale and speed with our graph data platform.https://neo4j.com/ &#xff08;二&#xff09;安装并配…

JavaWeb--HTML

一&#xff1a;HTML简介 *HTML是一门语言&#xff0c;所有的网页都是用HTML这门语言编写出来的&#xff1b; *HTML&#xff1a;超文本标记语言&#xff1b; 超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还能定义图片&#xff…

Java NIO浅析

NIO&#xff08;Non-blocking I/O&#xff0c;在Java领域&#xff0c;也称为New I/O&#xff09;&#xff0c;是一种同步非阻塞的I/O模型&#xff0c;也是I/O多路复用的基础&#xff0c;已经被越来越多地应用到大型应用服务器&#xff0c;成为解决高并发与大量连接、I/O处理问题…

linux上MySQL的安装

(1)解压安装包 tar -xzvf mysql-5.7.33-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.33-linux-glibc2.12-x86_64 /usr/local/mysql(2)创建数据目录 [roothecs-161929 3306]# mkdir -p /data/mysql/3306/data [roothecs-161929 3306]# mkdir -p /data/mysql/3306/binlog [roo…

章鱼网络 Community Call #19|​开启与 Eigenlayer 的合作

香港时间2024年3月8日12点&#xff0c;章鱼网络举行第19期 Community Call。 在过去的一个月&#xff0c;章鱼网络在成功完成 $NEAR Restaking 功能的安全审计之后&#xff0c;一直在稳步吸引关注。事实上&#xff0c;在整个行业中&#xff0c;我们是极少数已经推出 Restaking …

哔哩哔哩后端Java一面

前言 作者&#xff1a;晓宜 个人简介&#xff1a;互联网大厂Java准入职&#xff0c;阿里云专家博主&#xff0c;csdn后端优质创作者&#xff0c;算法爱好者 最近各大公司的春招和实习招聘都开始了&#xff0c;这里分享下去年面试B站的的一些问题&#xff0c;希望对大家有所帮助…

STM32中MicroLIB的关闭为什么会导致卡死----解析

STM32MicroLIB 大家好我是 MHZ 。最近又开始往回捡单片机的知识了~ 之前大学的时候都没用过 STM 的 CubeMX&#xff0c;这会拿来用着感觉很方便啊~ 果然科技在进步&#xff01; 在开发使用 Keil 对 STM32 进行开发的时候在会有一个叫做 MicroLIB 的选项。 这个的具体原因我搜…

ros、c++基于类的编程基础

基于class的编程结构&#xff0c;中间穿插ros的话题发布机制。 首先建立功能包&#xff1a; catkin_create_pkg control geometry_msgs message_generation message_runtime nav_msgs roscpp rospy std_msgs以上依赖基本上是大多数的ros消息所需要的依赖了。 然后确定我们的…

科研绘图一:箱线图(添加贝赛尔曲线)

R语言绘图系列—箱线图贝赛尔曲线 &#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&#xff09; 文章目录 R语言绘图系列---箱线图贝赛尔曲线&#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&…

pytorch CV入门 - 汇总

初次编辑&#xff1a;2024/2/14&#xff1b;最后编辑&#xff1a;2024/3/9 参考网站-微软教程&#xff1a;https://learn.microsoft.com/en-us/training/modules/intro-computer-vision-pytorch 更多的内容可以参考本作者其他专栏&#xff1a; Pytorch基础&#xff1a;https…

主干网络篇 | YOLOv8更换主干网络之ShuffleNetV2

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。ShuffleNetV2是一种轻量级的神经网络架构&#xff0c;用于图像分类和目标检测任务。它是ShuffleNet的改进版本&#xff0c;旨在提高模型的性能和效率。ShuffleNetV2相比于之前的版本&#xff0c;在保持模型轻量化的同时&am…

centos命令history设置记录10000行

今天在操作服务器的时候&#xff0c;用history查看操作记录的时候&#xff0c;发现只能查看10条&#xff0c;这样不行啊&#xff0c;我想查看所有人对服务器操作的命令。 [rootbogon ~]# history解决办法&#xff1a; #1、找到/etc/profile文件中的histsize 把10改成10000 […

机器学习周报第33周

目录 摘要Abstract一、文献阅读1.1 论文标题1.2 论文摘要1.3 论文背景1.4 过去研究1.5 论文介绍1.5.1 论文模型1.5.2 时空交互学习模块&#xff08;Spatiotemporal Interactive Learning Module&#xff09;1.5.3 动态图推理模块&#xff08;Dynamic Graph Inference Module&am…

ISIS接口认证实验简述

默认情况下&#xff0c;ISIS接口认证通过在ISIS协议数据单元&#xff08;PDU&#xff09;中添加认证字段&#xff0c;例如&#xff1a;一个密钥或密码&#xff0c;用于验证发送方的身份。 ISIS接口认证防止未经授权的设备加入到网络中&#xff0c;并确保邻居之间的通信是可信的…

java的前缀和算法

前缀和的概念 对于一个给定的数组A&#xff0c;它的前缀和数组S中S[i]表示从第1个元素到第i个元素的总和&#xff0c;用公式表示为&#xff1a; SiA1A2A3...An 前缀和的作用 在O(1)的时间求出数组任意区间的区间和。 降低求解的复杂度 算法模板 int n10; int [] arrnew in…