C++类和对象(下) 初始化列表 、static成员、友元、内部类等等

news2024/9/29 23:37:59

1.再探构造函数

  1. 之前使用构造函数时都是在函数体内初始化成员变量,还有一种构造函数的用法,叫做初始化列表;那么怎么使用呢?
    1. 使用方法用冒号开始(" : ")要写多个就用逗号(" , ")隔开数据成队列
    2. 每个成员变量后面跟着一个括号,括号内就是初始化的内容;括号内可以是初始值或者一个表达式
  2. 每个成员变量只要初始化一次就行了,不能重复初始化;这里也是成员变量定义的地方
  3. 引用成员变量、const成员变量、没有默认构造的类 类型变量,这三类必须在初始化列表位置进行初始化,否则会报错
  4. C++11支持在成员变量声明的位置给上缺省值,这里的缺省值是给初始化列表使用的,当没有显示写初始化列表就会用到缺省值
  5. 下列是思维动图形式的小总结
  6. 初始化列表中是按照成员变量声明处的顺序来进行初始化的,与初始化列表中写的先后顺序无关,所以建议声明的顺序和初始化列表的保持一致;当然也是和内存存储顺序有关(地址处低到高)
  7. 每个构造函数都有初始化列表,没有显示写会自动生成
  8. 补充:
    1. 函数的缺省值是提供给实参使用的,成员变量处的缺省值是提供给初始化列表使用的
    2. 调用函数建立栈帧时对象就会申请空间,构造函数是用来初始化的;两者之间要区分

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int day = 100)
	{
		_day = day;
	}
private:
	int _day;
};
class A
{
public:
	A(int a = 1, int b = 1, int c = 1)
		: _a(a)
		, _b(b)
		,_ca(c)
		,_quote(a)
		, _ptr((int*)malloc(12))//还可以是表达式
	{
		if (_ptr == nullptr)
		{
			perror("_prt fail");
		}
		else
		{
			memset(_ptr, 1, 12);//给内存初始化为 全1
		}
	}
	void Print()
	{
		cout << _a << '/' << _b << '/' << _c << endl;
	}
private:
    //声明
	int _a = 10; // 这里的是给初始化列表使用的缺省值
	int _b = 20;
	int _c = 30; //没有显示写时,缺省值就会用上
	int* _ptr;

	//下列的都必须使用初始化列表
	const int _ca;//必须要有初始化
	int& _quote;
	Date _ddy;// 没有默认构造会报错
};

int main()
{
	A aa(2024,8,23);
	aa.Print();

	return 0;
}

2.类型转换

  1. C++支持内置类型隐式类型转换为类类型对象需要对应的内置类型为参数(实参)的构造函数
  2. 如果不需要隐式类型转换了,构造函数前面加explicit就不再支持隐式类型转换
  3. 类类型的对象之间也可以隐式转换,需要对应构造函数支持
class AB
{
public:
	//explicit AB(char a = 0, char b = 0)
	AB(char a = 0,char b = 0)
	{
		_a = a;
		//_b = b;
	}
	void Print()
	{
		cout << _a << _b << endl;
	}
private:
	char _a;
	char _b = 'b';
};
class Stack
{
public:
	Stack()
	{

	}
	void Push(const AB& abp)
	{
		//...
	}
private:
	AB _arr[10];
	int _size;
};
int main()
{
	//隐式类型转换
	//65构造AB类的临时对象,然后用这个临时对象拷贝给au
	//编译器将 连续构造 + 拷贝构造 ,优化成直接构造
	AB au = 65;
	//AB au = "abcd";字符串不可以隐式类型转换
	au.Print();

	AB& aup = au;
	const AB& p = 2; // 2 会产生临时对象,具有常性,需要const


	Stack st;
	AB aa1(60, 70);
	st.Push(aa1);

	AB aa2 = { 88,99 };//支持多参数
	
	// C++11 才支持的
	const AB& aa3 = { 77,78 };

	//上面的方法麻烦,还需要创建类再插入;这里可以直接插入
	//因为AB是多参数的
	st.Push({ 77,78 });
	return 0;
}

3.static成员

  1. 用static修饰的成员变量, 叫做静态成员变量,静态成员变量一定要在类外面初始化
  2. 静态成员变量为 所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区;当前同类型的对象也可以访问
  3. 被static修饰的成员函数,叫静态成员函数,静态成员函数是没有this指针的;如果要返回静态成员变量需要静态成员函数
  4. 静态成员函数中可以访问其他的静态成员,但是不能访问非静态成员,因为没有this指针
  5. 非静态的成员函数,可以任意访问静态成员函数和静态成员变量
  6. 突破类域的限制就可以访问静态成员,通过 fun3::Getb() 或者 fufu.Getb();就可以访问静态成员函数和静态成员变量
  7. 虽然可以任意访问静态成员,但静态成员也是类的成员,也会受到public、protected、private访问限定符的限制
  8. 静态成员变量,不走构造函数的初始化列表;因为不属于某一个类,自然给初始化列表的缺省值也不能用
class fun2
{
public:
	static int _aa;

	fun2()
	{

	}
	void sum()
	{
		_a--;
	}
	static int Geta()//不可以访问非静态的,非静态的可以访问静态的
	{
		_a += 2;
		return _a;//虽然都可以访问静态成员变量,但是正常情况下是需要this指针才能当作返回值
		//得出如果要返回静态成员变量需要静态成员函数
	}
private:
	static int _a;
};

class fun3
{
public:
	static int Getb()
	{
		//调用函数 返回结果
		return fun2::Geta();//因为是静态函数所以都可访问,但也受访问限定符限制 
	}
private:
	static int _b;
};

//在类外初始化
int fun3::_b = 30;

int fun2::_aa = 22;
int fun2::_a = 10;
int main()
{
	int at = 1;
	fun2 f1;
	f1.sum();
	cout << fun2::Geta() << endl;

	//大小是1,标识这个对象,静态成员变量在静态区
	cout << sizeof(f1) << endl;

	//都可以访问静态函数
	cout << fun3::Getb() << endl;

	//所有类都可以访问,但是会受到类域限制
	cout << fun2::_aa << endl;


	//访问静态成员函数两种方式,常用 :: 符号访问
	fun3 fufu;
	cout << fun3::Getb() << endl;
	cout << fufu.Getb() << endl;
	return 0;
}

补充:

  1. 静态的变量第一次走到那里才会初始化;全局的静态会在,main函数之前初始化。
  2. 编译阶段会有语法检查;变量初始化时,如果不使用会被优化;

4.友元

  1. 友元提供一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明前面加上friend,把友元声明放到类里
  2. 外部的友元函数可以访问类的私有和保护成员,就是一个声明,不是类的成员,也不会占用额外空间
  3. 友元函数可以在类的任何地方定义,不受类访问限定符限制;一般都放到类的最开头
  4. 一个函数可以是多个类的友元函数
  5. 友元类中的成员函数都可以是另一个类的友元函数,可以访问另一个类的私有和保护
  6. 不过友元类是单向的,A类是B类的友元,但是B类不可以是A类的友元
  7. 友元类没有传递性,A类是B类的友元,B类可以是C类的友元,但是A类不可以是C类的友元;如果希望是那么在C类写上A类的友元声明
  8. 友元也有弊端,友元会增加耦合度,破坏了封装,尽量不用
//前置声明
void Print();
class yy
{
	//友元函数声明
	friend int sum(const yy& x1, const yy& x2);
	friend void Print();

	//友元类声明,单向的;yy类不可访问cl类
	friend class cl;
public:
	void print2()
	{
		Print();
	}
private:
	int _a = 10;
	int _b = 20;
};

int sum(const yy& x1,const yy& x2)
{
	
	return x1._a + x2._b;
}

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

class cl
{
	int sum1(const yy& x1, const yy& x2)
	{

		return x1._a + x2._b;
	}
	int sum3(const yy& x1, const yy& x2)
	{
		return x1._a + x2._b;
	}
};

int main()
{
	yy x1, x2;

	int ret = sum(x1, x2);

	cout << ret << endl;

	//此时证明写了友元函数声明,可以双方互相访问
	x1.print2();
	return 0;
}

5.内部类

  1. 如果一个类定义在另一个类的内部,那么这个类就叫做内部类;内部类是一个独立的类(计算外部类的空间时,不会开辟内部类的),只受到访问限定符的限制和外部类的类域限制,所以外部类定义的对象中不包含内部类
  2. 内部类本质也是一种封装,但A类和B类达成合作关系(B类频繁使用A类),此时可以考虑写一个专属内部类,就是把A类放到private/protected位置;这样其他地方想创建A类,就创建不了
  3. 可以看看这题,更好的理解

#include <iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << _a << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a = 10;
	class B
	{
	private:
		int _x;
		double _y;
	};
};
//匿名对象
int main()
{
	A aa;
	aa.Print();
	//A aa(); //此时不确定是函数还是类

	cout << sizeof(aa) << endl;//计算的大小是4,并不包含B类
	return 0;
}

6.匿名对象

  1. 用类型(实参),这种对象叫做匿名对象;之前的那种是有名对象 类型 对象名(实参),是有名对象
  2. 匿名对象的生命周期只有一行,和编译器生成的临时对象一样;一般临时用一下,可以用匿名对象
#include <iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << _a << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a = 10;
};

int main()
{

	A aa;
	aa.Print();
	//A aa(); //此时不确定是函数还是类

	//匿名对象,和临时对象一样声明周期只有一行,
	A();

	//匿名对象,可以直接调用函数;对比上面要少写一行
	A().Print();
	return 0;
}

7.对象拷贝时的编译器优化

  1. 现在的编译器为了提高程序的效率,不影响正确性的情况下都会进行优化;尽可能减少传值和传返回值的过程可省略的部分
  2. 有像 构造临时对象 + 拷贝构造 优化成直接构造
  3. 至于怎么优化看各自的编译器,C++并没有严格的规定;当前主流的比较新的编译器会优化连续拷贝并进行合并优化,有些编译器会更加激进的合并优化
  4. 初始化对象时,强制类型转换也是根据构造函数来看的,是需要看构造函数的参数的
#include <iostream>
using namespace std;
class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a = 1)" << endl;
	}
	//拷贝构造
	A(const A& xx)
		:_a(xx._a)
	{
		cout << "A(const A & xx)" << endl;
	}
	~A()
	{
		cout << _a << "~A()" << endl;
	}
	void Print()
	{
		cout << _a << endl;
	}
	A& operator++()
	{
		_a+= 100;
		return *this;
	}

	A& operator=(const A& xx)
	{
		cout << "A& operator=(const A& xx)" << endl;
		if (this != &xx)//不能和自己相同,否则就不对了
		{
			_a = xx._a;
		}
		return *this;
	}
private:
	int _a;
};

void fun1(A aa)
{
	
}

//初始化对象时
//int main()
//{
//	A tmp = 1;// 构造一个临时对象 + 拷贝构造  都会转化成直接构造
//
//	const A& at = 10;//直接会强制类型转换,这个强制类型转换也是根据构造函数来看的
//	return 0;
//}

//传参时的优化
//int main()
//{
//	A aa(1);
//	fun1(aa);//没有使用引用,会产生拷贝构造,出函数临时对象销毁
//
//	cout << endl;
//
//	//有优化
//	fun1(A(20));
//
//	cout << endl;
//
//	//有优化
//	fun1(30);//构造临时对象 + 拷贝构造 优化成直接构造
//	return 0;
//}


//返回值
A fun2()
{
	A aa(1);
	++aa;
	cout << "------" << endl;
	return aa;//vs2022 优化比19版 更加激进;把aa的构造,拿101直接构造临时对象,并作为返回值
}

//int main()
//{
//	fun2().Print();//使用函数的临时对象调用函数,并且临时对象的生命周期只在这一行
//	cout << "********" << endl;
//	return 0;
//}

int main()
{
	A ret;
	ret = fun2();// 拿临时对象去赋值拷贝构造; 只优化了fun2()的拷贝构造
	ret.Print();

	cout << "**********" << endl << endl;
	return 0;
}

做好自己,减少竞争性的努力,走好自己的路,超越昨天的自己

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

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

相关文章

DC00023基于jsp+MySQL新生报到管理系统

1、项目功能演示 DC00023基于jsp新生报到管理系统java webMySQL新生管理系统 2、项目功能描述 基于jspMySQL新生报到管理系统项目分为学生、辅导员、财务处和系统管理员四个角色。 2.1 学生功能 1、系统登录 2、校园新闻、报到流程、学校简介、在线留言、校园风光、入校须知…

解决Qt每次修改代码后首次运行崩溃,后几次不崩溃问题

在使用unique_ptr声明成员变量后&#xff0c;我习惯性地在初始化构造列表中进行如下构造&#xff1a; 注意看&#xff0c;我将m_menuBtnGroup的父类指定为ui->center_menu_widget&#xff0c;这便是导致崩溃的根本原因&#xff0c;解决办法便是先用this初始化&#xff0c;后…

pdf页面尺寸裁减

1、编辑pdf 2、点击裁减页面&#xff0c;并在空白区域双击裁减 3、输入裁减数据&#xff1a;

calibre-web浏览器标题icon修改

calibre-web浏览器标题icon修改 Windows安装calibre-web&#xff0c;Python-CSDN博客文章浏览阅读537次&#xff0c;点赞10次&#xff0c;收藏11次。pip install calibreweb报错&#xff1a;error: Microsoft Visual C 14.0 or greater is required. Get it with "Microso…

Springboot中基于注解实现公共字段自动填充

1.使用场景 当我们有大量的表需要管理公共字段&#xff0c;并且希望提高开发效率和确保数据一致性时&#xff0c;使用这种自动填充方式是很有必要的。它可以达到一下作用 统一管理数据库表中的公共字段&#xff1a;如创建时间、修改时间、创建人ID、修改人ID等&#xff0c;这些…

视频剪辑软件哪个好?剪辑更高效用这些

众所周知&#xff0c;视频已经成为我们记录生活、表达自我的重要方式。 无论是制作旅行Vlog&#xff0c;还是剪辑短片分享故事&#xff0c;优秀的视频剪辑软件是让创意变为现实的利器。 那么&#xff0c;如何在众多免费软件中做出明智选择&#xff0c;成为剪辑高手呢&#xf…

通信工程学习:什么是SISO单入单出

SISO&#xff1a;单入单出 SISO&#xff0c;即单输入单输出&#xff08;Single-Input Single-Output&#xff09;系统&#xff0c;也被称为单变量系统。在这种系统中&#xff0c;输入量与输出量各为一个&#xff0c;是控制理论中的一个基本概念。以下是对SISO系统的详细解释&am…

为什么说函数传递参数最好小于四个

有一个说法说是函数传递参数最好不超过四个&#xff0c;原因有一个是参数太多难以维护&#xff0c;另一个重要的原因就是函数传递小于四个参数时候效率会更高&#xff0c;其实这个说法也不全对&#xff0c;在不同的结构下不太一样&#xff0c;也不一定是4 其实那么下面将探究函…

【RocketMQ】消费失败重试与死信消息

&#x1f3af; 导读&#xff1a;本文档详细介绍了RocketMQ中的重试机制与死信消息处理方法。对于生产者而言&#xff0c;文档提供了如何配置重试次数的具体示例&#xff1b;而对于消费者&#xff0c;它解释了默认情况下消息消费失败后的重试策略&#xff0c;并展示了如何通过代…

STM32LL库之printf函数重定向

1. 加入以下代码 int fputc(int ch,FILE *f) {LL_USART_TransmitData8(USART1,ch);while(!LL_USART_IsActiveFlag_TXE(USART1));//需要等待发送完成return(ch); }记得添加 stdio.h 头文件 2. 在MDK中勾选&#xff1a;Use MicroLIB

C++【类和对象】(取地址运算符重载与实现Date类)

文章目录 取地址运算符重载const成员函数取地址运算符重载 Date类的实现Date.hDate.cpp1.检查日期合法性2. 构造函数/赋值运算符重载3.得到某月的天数4. Date类 - 天数的操作4.1 日期 天数4.2 日期 天数4.3 日期 - 天数4.4 日期 - 天数 5. Date的前后置/--5.1 前置5.2 后置5.…

学习鸿蒙HarmongOS(基础一)

最近听到一个朋友在干鸿蒙系统开发&#xff0c;于是我也来看看&#xff0c;我看到的第一感觉和前端TS好像&#xff0c;鸿蒙的是叫ArkTS&#xff0c;于是来看一下视频&#xff0c;学习了一下&#xff0c;我的随手笔记记录一下吧,方便我以后阅读 基本 语句 函数

unity3D雨雪等粒子特效不穿透房屋效果实现

做项目有时候会做天气模拟&#xff0c;模拟雨雪天气等等。但是容易忽略一个问题&#xff0c;就是房屋内不应该下雨或者下雪&#xff0c;这样不就穿帮了嘛。 下面就粒子穿透物体问题做一个demo。 正常下雨下雪在室内的话&#xff0c;你可以看到&#xff0c;粒子是穿透建筑的。…

【C++篇】启航——初识C++(上篇)

目录 引言 一、C的起源和发展史 1.起源 2.C版本更新 二、C在⼯作领域中的应⽤ 三、C入门建议 1.参考文档 2.推荐书籍 四、C的第一个程序 1.C语言写法 2.C写法 五、命名空间 1.为什么要有命名空间 2.定义命名空间 3.主要特点 4.使用示例 六、C输⼊&输出 …

C程序设计——结构化程序设计的三种结构

前面我说过&#xff1a;“结构化编程语言&#xff0c;用语法限制程序员&#xff0c;只能使用顺序、选择、循环三种结构来解决问题。” 接下来&#xff0c;就讲解这三种结构。 顺序结构 前面我讲过&#xff0c;C语言所有的程序&#xff0c;都必须有一个 main 函数&#xff0c…

TCP\IP标准与OSI标准

TCP/IP 模型和 OSI 模型都是用于描述网络体系结构的模型&#xff0c;但它们的设计理念和层次结构有所不同。TCP/IP 模型更注重实际实现&#xff0c;而 OSI 模型更注重抽象和标准化。 1. OSI 模型 (Open Systems Interconnection Model) OSI 模型是一个七层模型&#xff0c;从…

828华为云征文|部署在线论坛网站 Flarum

828华为云征文&#xff5c;部署在线论坛网站 Flarum 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 Flarum3.1 Flarum 介绍3.2 Flarum 部署3.3 Flarum 使用 四、总结 一、…

针对考研的C语言学习(定制化快速掌握重点2)

1.C语言中字符与字符串的比较方法 在C语言中&#xff0c;单字符可以用进行比较也可以用 > , < ,但是字符串却不能用直接比较&#xff0c;需要用strcmp函数。 strcmp 函数的原型定义在 <string.h> 头文件中&#xff0c;其定义如下&#xff1a; int strcmp(const …

Vue.js组件开发指南

Vue.js组件开发指南 Vue.js 是一个渐进式的 JavaScript 框架&#xff0c;用于构建用户界面。它的核心是基于组件的开发模式。通过将页面分解为多个独立的、可复用的组件&#xff0c;开发者能够更轻松地构建复杂的应用。本文将深入探讨 Vue.js 组件开发的基础知识&#xff0c;并…