类与对象(下)

devtools/2025/1/16 3:37:10/

目录

前言

一、再谈构造函数

二、static成员

三、友元

四、内部类

五、匿名对象

总结:


 

前言

终于来到了我们类和对象的最后一部分,类与对象下。在这部分中,我们主要讲解类与对象的“插件” ,也就类似于拓展部分,比如友元函数,初始化列表等,让大家对C++类与对象有一个更深层次的理解。

一、再谈构造函数

在创建对象时,编译器通过调用构造函数,给对象中的每个成员一个合适的初始值。虽然调用了构造函数之后对象中已经有了一个初始值,但这并不能叫做对对象中的成员变量初始化,构造函数函数体内的部分只能称作赋初值。因为构造函数体内可以多次赋值,但初始化只能初始化一次。

在类中负责初始化工作的是初始化列表,是一个:以一个冒号开始,以逗号分隔的成员数据列表,每个成员变量后面跟着一个放在括号中的初始值或者表达式

//以日期类为例
class Date
{
public:Date(int year, int month, int day)//以一个冒号开始,以逗号分隔的成员数据列表,每个成员变//量后面跟着一个放在括号中的初始值或者表达式。:_year(year),_month(month),_day(day){}
private:int _year;int _month;int _day;
};

每一个成员变量在初始化列表中只会出现一次(因为初始化只会进行一次),初始化列表就是成员变量初始化的地方。

注意,如果类中的成员变量包含如下类型,就必须把此变量放在初始化列表中进行初始化:

1、引用成员变量

2、const 成员变量

3、自定义类型成员(尤其是此类型没有默认构造函数时)

class A
{
public:A(int a):_a(a){}
private:int _a;
};class B
{
public:B(int a, int ref):_obj(a),_ref(ref), _n(10){}
private:A _obj;// 没有默认构造函数int& _ref; // 引用const int _n; // const 
};

 虽然没有强制要求其他类型也要写进初始化列表初始化,但在尽量用初始化列表,因为对于自定义类型来说,它一定会先使用初始化列表初始化。

另外,初始化列表中的初始化顺序与它在初始化列表中的先后顺序无关,而与它在类中声明的先后相关,越先声明,越先在初始化列表中初始化。

class A
{
public:A(int a1):_a1(a1), _a2(_a1){}void Print(){cout << _a1 << ' ' << _a2 << endl;}
private:int _a2;int _a1;
};int main()
{A a(1);a.Print();return 0;
}

从这段代码中可以看出,我们在类中先声明了_a2,随后声明了_a1,在初始化列表中,我们先初始化_a1,随后用_a1给_a2初始化。倘若初始化顺序是按照在初始化列表中的先后顺序,这段代码的结果就会是两个1,但最后却是:

这就可以说明,初始化列表中的初始化顺序与它在初始化列表中的先后顺序无关!

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

class Date
{
public:Date(int year, int month = 1, int day = 1):_year(year), _month(month), _day(day){}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date a(1900);Date b = 2023;//1b.Print();a.Print();a = 2024;//2a.Print();return 0;
}

这里的1与2过程其实都是发生了隐式类型转换。实际上编译器背后会用2023,2024构造一个无名的对象,最后用无名对象给a与b赋值。

但倘若我们给构造函数前面加一个关键字explicit,就会编译报错,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作用。

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

二、static成员

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

用static修饰计算有多少个类:
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;cout << A::GetACount() << endl;A a3(a1);cout << A::GetACount() << endl;
}int main()
{TestA();cout << A::GetACount() << endl;return 0;
}

static修饰的类的静态成员有以下特性:

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

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

3、类静态成员可以通过类名::静态成员或者对象.静态成员来访问。

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

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

静态成员函数可以调用非静态成员函数,但是需要通过对象来调用非静态成员函数,因为非静态成员函数属于对象而不属于类。而非静态成员函数可以调用类的静态成员函数,因为静态成员函数属于类而不属于对象。

三、友元

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

友元分为友元函数友元类

1、友元函数

当我们发现对象与隐含的this指针在抢占第一个参数的位置时,但实际我们需要的第一个参数是形参参数,封装成全局函数又不好解决问题的时候,就可以通过友元函数来解决了。
友元函数可以访问该类的私有成员,他是一个定义在类外部的普通函数,不属于任何类,但需要在所使用的类内部声明,声明时加上关键字friend表示此函数为该类的友元函数。(译为你是我的朋友)

注意:

友元函数可以访问类的私有与保护成员,但不属于类的函数。

友元函数不能被const修饰(因为const不属于类成员函数,不受const限制)

友元函数可以在类定义的任何地方声明,不受访问限制符的限制(因为友元函数不属于类的成员函数)

一个函数可以是多个类的友元函数(我可以是很多人的朋友)

2、友元类

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

注意:

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

比如我们定义了一个A类与一个B类,在A类中我们声明B类为A类的友元类,那么我们就可以在B类中直接访问A类的非公有成员,但是A类不可以访问B类的非公有成员(我认为你是我的朋友,但别人不认为你是他的朋友)

2、友元关系不能传递:

倘若C是B的友元类,B是A的友元类,那么不能说明C是A的友元类.

3、友元关系不能继承


class Time
{friend class Date;//声明Date类是Time类的友元类,则可以直接在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){//Date类的成员函数,却可以直接访问Time类里的私有变量_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

四、内部类

概念倘若一个A类定义在一个B类的内部,那我们就称A为B的内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。因为外部类对内部类没有任何优越的访问权限。恰恰相反,内部类天生是外部类的友元类,内部类可以访问外部类的非公有成员。

特性:
1、内部类可以定义在外部类的任意位置,不受public,private,protected的限制。
2、内部类可以直接访问外部类的成员(包括static,且使用静态变量时不需要外部类的对象/类名).
3、用sizeof(外部类)的时候不会受到内部类的影响,计算的还是外部类的大小。
class Time
{class Date//天生是Time的友元类{public:Date(int year = 1900, int month = 1, int day = 1):_year(year), _month(month), _day(day){}void Couttime(const Time&a){//Date类的成员函数,却可以直接访问Time类里的私有变量cout << a._hour << ' ' << a._minute << ' ' << a._second;cout << k;}private:int _year;int _month;int _day;};
public:Time(int hour = 0, int minute = 0, int second = 0):_hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;static int k;
};

五、匿名对象

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

 运行代码,我们可以看见:

通过A();我们生成了一个匿名的对象,这个匿名对象的生命周期只有这一行,过后就会自动调用析构函数。

总结:

通过这三节的讲解,大家要知道, 类就是对某一类实体 ( 对象 ) 来进行描述的,描述该对象具有那
些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化
具体的对象

通过理解类与对象的概念,我们可以更好地应用面向对象的设计思想来解决实际问题,提高代码的质量和效率。同时,也能够更好地理解和应用面向对象编程语言中的各种特性和技术,如继承、多态、封装等。

在学习过程中,我们可能会遇到各种挑战和困难,但只要保持学习的态度,不断探索和实践,就能够不断提升自己的编程水平。希望本文能够为您对类与对象的理解提供一些帮助,并在您的编程之旅中起到一定的指导作用。


http://www.ppmy.cn/devtools/30969.html

相关文章

lxml 在 Windows 7上安装无法安装怎么办?

lxml 在 Windows 7上安装无法安装怎么办&#xff1f; 要在Windows 7上安装lxml&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装Python&#xff1a; 如果您的计算机尚未安装Python&#xff0c;请先安装Python。您可以从Python官方网站下载Windows安装程序&#xff0c…

【linux】重定向

重定向 什么是重定向如何实现一个简单的重定向关于重定向的系统调用接口 注意&#xff1a;在看这篇博客之前&#xff0c;最好是要对文件在系统中是如何被打开的以及操作系统是如何管理文件有一个初步了解&#xff0c;如果不了解的话&#xff0c;可以看看这篇博客《初步认识文件…

【Spring AI】聊天API-OpenAI-Function Call

文章目录 Function Calling工作原理快速上手将函数注册为 Bean纯 Java 函数实现&#xff08;Plain Java Functions&#xff09;FunctionCallback Wrapper Specifying functions in Chat OptionsRegister/Call Functions with Prompt Options 附录&#xff1a;Spring AI 函数调用…

MySQL Binlog 闪回与分析

文章目录 前言1. 修改 event 实现闪回1.1 binlog 结构1.2 闪回案例1.3 方法总结 2. 解析文本闪回2.1 mysqlbinlog2.2 闪回案例2.3 方法总结 3. 在线订阅闪回3.1 mysql-replication3.2 binlog2sql3.3 方法总结 4. Binlog 分析方法4.1 分析场景4.2 辅助定位事务4.3 方法总结 5. 平…

uniapp关于iconfont字体图标使用

1、打开[阿里巴巴矢量图标库](https://www.iconfont.cn/)&#xff0c;选择需要的图标添加到购物车 2、点开购物车&#xff0c;将图标添加到项目 3、点开项目&#xff0c;点击下载至本地&#xff0c;会得到一个download.zip包 4、解压download包 5、将包里的iconfont.css和iconf…

408数据结构-二叉树的概念、性质与存储结构 自学知识点整理

前置知识&#xff1a;树的基本概念与性质 二叉树的定义 二叉树是一种特殊的树形结构&#xff0c;其特点是每个结点至多只有两棵子树&#xff08;即二叉树中不存在度大于 2 2 2的结点&#xff09;&#xff0c;并且二叉树是有序树&#xff0c;左右子树不能互换。 与树类似&#…

Javascript:Web APIs(一)

Javascript基础&#xff08;一&#xff09; Javascript基础&#xff08;二&#xff09; Javascript基础&#xff08;三&#xff09; Javascript基础已经结束&#xff0c;接下来我们将进入到整个Web API学习中&#xff0c;在此&#xff0c;我们将学习DOM操作&#xff0c;基本的…

border 是渐变色怎么设置 圆角 radius?

什么&#xff1f;渐变色的 border 无法设置 radius&#xff1f; 你5年开发了。ui 能做出的东西&#xff0c;你说你做不出来&#xff1f; .box {width: 100px;height: 150px;position: relative;z-index: 1;&::after {content: "";position: absolute;inset: 0;…