类 —— 友元、常/静态成员函数

类的大小

和结构体大小求法一致。但需注意,普通空类也会占用 1 字节大小,因为普通空类可以实例化对象。
在这里插入图片描述

而 抽象空类占 4 字节(32 位机中),因为抽象空类中含有虚指针(含有虚函数的非抽象空类同理)。
在这里插入图片描述

友元

某些情况下,需要频繁读写类的数据成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。引入友元。

友元函数

全局函数作为类的友元函数

用到哪一个类中的私有成员,就把全局函数的“声明”前加上 friend,放到哪一个类中就可以了。
全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
为避免不必要的麻烦,全局函数写在类的声明下面。一个全局友元函数理论上来说可以访问多个类。

#include <iostream>
#include <math.h>
using namespace std;class N;class M
{float x;
public:M() { }M(float x):x(x) { }friend double func(M m, N n);		// 声明全局函数 func 是 M 的友元函数// 全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
};class N
{float y;
public:N() { }N(float y):y(y) { }friend double func(M m, N n);		// 声明全局函数 func 是 N 的友元函数// 全局函数的“声明”可以放置到类中任何位置,不受权限修饰符的影响
};double func(M m, N n)
{return sqrt(m.x*m.x + n.y*n.y);
}int main()
{M m(3);N n(4);cout << "√3²+4² = " << func(m, n) << endl;return 0;
}

在这里插入图片描述

一个类中成员函数作为另一个类的友元函数

如果 M 类中的函数,是 N 类的友元函数(由 N 声明),则 M 中的函数可以访问 N 中的私有成员。
1、需要把类做前置声明,但是类的前置声明仅说明可以使用类定义变量/形参,并不能声明类中的成员。
2、如果一个类中的成员函数,作为另一个类的友元,该类的成员函数必须在类内声明,类外定义

#include <iostream>
using namespace std;class N;class M
{float x;
public:M() { }M(float x):x(x) { }void show(N n);			// 想要此函数作为 N类 的友元函数,须类内声明,类外定义
};class N
{float y;
public:N() { }N(float y):y(y) { }friend void M::show(N n);		// 把 M 中的 show 函数,声明为了 N 中的友元
};void M::show(N n)			// 因为此函数是其他类N的友元,所以要在类外定义
{cout << "It's a function belongs to class M. " << endl;cout << "But it can use variables of class N: " << n.y << endl;// 访问另一个类 N 中的私有成员
}int main()
{M m;N n(4);m.show(n);				// M 中的函数可以访问 N 中的私有成员return 0;
}

在这里插入图片描述

友元类

如果 M类 中,声明了 friend class N,则称 M类 把 N类 作为友元类。N类 中的所有成员都能访问 M类 中的私有成员。
如果 N类 中,声明了 friend class M,则称 N类 把 M类 作为友元类。M类 中的所有成员都能访问 N类 中的私有成员。
如果声明友元类,就不涉及到类中成员的问题,写代码时不需要考虑类的先后顺序。但后写的类要提前声明。

#include <iostream>
#include <math.h>
using namespace std;class N;class M
{float x;
public:M() { }M(float x):x(x) { }void show(N n);void show();
};void M::show()
{cout << "This is a function belongs to class M: " << this->x << endl;
}class N
{float y;
public:N() { }N(float y):y(y) { }friend class M;
};void M::show(N n)
{cout << "This is a function belongs to class M. " << endl;cout << "But it can use variables of class N: " << n.y << endl;
}int main()
{M m(3);N n(4);m.show(n);cout << endl;m.show();return 0;
}

在这里插入图片描述

💡 练习

定义两个类 Dog 和 Cat 类,分别具有私有的成员属性:颜色、性别、年龄。
写出两个类的无参构造和有参构造,定义一个全局函数:计算猫和狗的年龄之和,并输出。
定义 Dog 类为 Cat 类的友元,在 Dog 类中定义一个 c_show 函数,输出猫和狗的颜色。

#include <iostream>
using namespace std;class Dog;class Cat				// 必须定义在 Dog 前
{string color;string gender;int age;
public:Cat(){cout << "A constructor of cat without arguments." << endl;}Cat(string color, string gender, int age):color(color), gender(gender), age(age){cout << "A constructor of cat with arguments." << endl;}friend int sum(Dog &d, Cat &c);friend class Dog;
};class Dog
{string color;string gender;int age;
public:Dog(){cout << "A constructor of dog without arguments." << endl;}Dog(string color, string gender, int age):color(color), gender(gender), age(age){cout << "A constructor of dog with arguments." << endl;}friend int sum(Dog &d, Cat &c);void c_show(Cat &c);
};void Dog::c_show(Cat &c)
{cout << "The cat is " << c.color<< "." << endl;			// Cat 必须定义在前面的原因cout << "The dog is " << this->color << "." << endl;
}int sum(Dog &d, Cat &c)
{return d.age + c.age;
}int main()
{Dog d("husky", "female", 5);Cat c("ragdoll", "male", 6);cout << "Age: " << sum(d, c) << endl;d.c_show(c);return 0;
}

在这里插入图片描述

友元的注意事项

1、类的前置声明,只能表明存在该类,并不能说明类中有哪些成员。
2、如果一个类的成员函数作为另一个类的友元,需要在该类内部声明,在该类外部定义。
3、友元是单向的,A 声明了 B 为友元类,B 中的成员可以访问 A 中的私有成员。但这并不意味着 A 中的成员,可以访问 B 中的私有成员。
4、友元没有传递性。A中:friend class B,且 B中:friend class C ≠ A 把 C 当做友元类
5、提高了程序的运行效率,但是破坏了类的封装性和隐藏性,使得非成员函数也能够访问类中的私有成员。导致程序的维护性变差,因此使用友元要慎重

A中:friend class B ⇔ A 声明了 B 为友元类 ⇔ A 把 B 当做友元类 ⇔ B 是 A 的友元类 ⇔ B 可访问 A

常成员变量 & 常局部变量

常成员变量

该成员变量的值无法被修改。
常成员变量的初始化方式有两种:
● 直接赋值
声明后赋值:
● 构造初始化列表
上述两种方式同时使用时,前者失效,以后者为准。

#include <iostream>
using namespace std;class Demo
{
private:// ● 定义时直接赋值const int a = 1;const int b = 2;const int c = 3;public:// ● 构造初始化列表的方式进行初始化Demo(int a, int b, int c):a(a), b(b), c(c) {}//    Demo(int a, int b, int c)
//    {
//        this->a = a;
//        this->b = b;
//        this->c = c;
//    }void show(){cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}void test()		// 错误{
//        a++;
//        b++;
//        c++;}
};int main()
{Demo d(10, 20, 30);			d.show();return 0;
}

常局部变量

该局部变量不可被修改,常用于引用参数。

#include <iostream>
using namespace std;class Demo
{
public:void test(){int a = 1;const int b = 2;a++;
//        b++; 			// 错误cout << "a = " << a << endl;cout << "b = " << b << endl;}};int main()
{Demo d;d.test();return 0;
}

常成员函数 & 常对象

对比普通常函数:const int add(int n1, int n2); ——> const 修饰函数的返回值

常成员函数

格式
返回值类型  成员函数名() const;
性质

1、常成员函数内,不能修改成员变量;
2、可以保护成员变量不被随意修改;
3、如果常成员函数的声明和定义分开,两个位置都要写上 const 修饰
4、不能调用非 const 的成员函数;
建议只要成员函数不修改成员变量就使用 const 修饰,例如 get 函数。

#include <iostream>
using namespace std;class Demo
{
private:int a;
public:Demo(int a){this->a = a;}void show(){cout << "哈哈哈哈哈" << endl;}// const 修饰的成员函数int get_a() const{return a;}void test() const{cout << get_a() << endl;
//        show(); 			// 错误
//        a++; 				// 错误cout << "a = " << a << endl;}
};int main()
{Demo d(1);cout << d.get_a() << endl;d.test();return 0;
}

在这里插入图片描述

常成员函数的 this 指针
const  类名  *const  this;

this 的指向和值均不能更改。
在这里插入图片描述

mutable(慎用)

取消常属性,在类中给成员变量加上 mutable 关键字,就允许在常成员函数内修改成员变量。

#include <iostream>
using namespace std;class M
{mutable float x;		// mutable 取消成员变量的常属性,可以在常成员函数中修改该成员变量
public:M() { }M(float x):x(x) { }void set_value(float val)const;void show();
};void M::set_value(float val)const
{this->x = val;
}void M::show()
{cout << this->x << endl;
}int main()
{M m(12);cout << "Before: ";m.show();m.set_value(5);cout << "After: ";m.show();return 0;
}

在这里插入图片描述

常对象

实例化对象时,前面加上 const 关键字修饰。
常对象的性质:
1、常对象只能调用常成员函数(默认调用的特殊成员函数除外);
2、非常对象既可以调用非常成员函数,也可以调用常成员函数;
3、常对象可以调用成员变量,但不能修改;
在这里插入图片描述

#include <iostream>
using namespace std;class M
{mutable float x;
public:M() { }M(float x):x(x) { }void set_value(float val)const;void show();void show(M m);
};void M::set_value(float val)const
{this->x = val;
}void M::show()
{cout << this->x << endl;
}void M::show(M m)
{cout << m.x << endl;
}int main()
{const M m1(12);m1.set_value(13);M m2(5);m2.show(m1);m2.show();m2.set_value(361);m2.show();return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;class Demo
{
private:int a;
public:int b = 10;Demo(int a){this->a = a;}void show(){cout << "哈哈哈哈哈" << endl;}// const修饰的成员函数int get_a() const{return a;}void test() const{cout << get_a() << endl;
//        show(); 		// 错误
//        a++; 			// 错误cout << "a = " << a << endl;}
};int main()
{// 两种初始化方式,两种等效const Demo d(1);
//    Demo const d(1);// 常对象可以调用 const 修饰的成员函数。cout << d.get_a() << endl;// 常对象不可以调用 非const 修饰的成员函数。
//    d.show();// 错误
//    d.b = 11;// 可以调用成员变量,但是不能做修改cout << d.b << endl;return 0;
}

在这里插入图片描述

💡 练习

封装圆类 Circle,私有属性半径 R,圆周率 PI,在程序运行过程中 PI 的值不变,写出圆的构造函数,公有的求面积函数。

#include <iostream>
#include <iomanip>
using namespace std;class Circle
{float radius;
//    mutable double PI;					// 方法一const double PI = 3.14159265359;		// 方法二,仅此一句
public:Circle() { }Circle(float radius):radius(radius) { }double area(Circle &cir){return cir.radius * cir.radius * PI;}// void set_pi()const					// 方法一// {//     PI = 3.14159265359;// }
};int main()
{Circle cir(2);
//    cir.set_pi();							// 方法一cout << setprecision(8) << "S = " << cir.area(cir) << endl;return 0;
}

在这里插入图片描述

静态局部变量、静态成员变量 & 静态成员函数

静态局部变量在第一次调用时创建,直到程序结束后销毁,同一个类的所有对象共用一份静态局部变量。
静态成员函数 和 静态成员变量,既不依赖类的对象而存在,也不占用类的空间。

静态局部变量(类的函数中:static 数据类型 变量名;)

#include <iostream>
using namespace std;class Test
{
public:void func(){int a = 1;				// a可能会在同一个地址反复创建销毁。static int b = 1; 		// 静态局部变量,同一个类的所有对象共用一份cout << "a = "<< ++a << "\t" << &a << endl;cout << "b = "<< ++b << "\t" << &b << endl;}};int main()
{Test t1;t1.func();Test t2;t2.func();t1.func();t2.func();return 0;
}

在这里插入图片描述

静态成员变量(类中:static 数据类型 成员名;)

1、静态成员变量 独立于类体存在;
2、每一个类对象公用同一个静态成员;
3、即使没有实例化类对象,也可以使用类中的静态成员;
4、静态成员一般是 public 权限;
5、静态成员需要在全局处声明,声明的同时可以进行初始化操作,不初始化默认为 0;
6、静态成员不占用类的大小;
● 在程序开始运行时就开辟内存,直到程序运行结束销毁。
● 虽然静态成员变量可以使用对象调用,但是更建议直接使用类名进行调用。
静态成员函数
1、不依赖于任何类对象(可以在没有类对象的情况下调用);
2、静态成员函数 没有 this 指针(因为可以不通过类对象被调用);
3、静态成员函数,不能直接使用非静态成员变量; ——> 间接使用方法见链接 (👈 链接至另一博主,放心跳转)
4、调用方式:① 直接通过类名加域限定符调用,② 通过类对象调用;
5、非静态成员函数和静态成员函数可以构成重载;
6、不能在静态成员函数中,调用同类中其他非静态成员函数;
7、若要 类内声明类外定义,声明 需要写 static 关键字,类外的实现不用写;

#include <iostream>
using namespace std;class Rectangle
{
public:static float height;float width;static void show(){//cout << "width = " << width << endl;      // 3、cout << "height = " << height << endl;}void show(float width)      // 5、{}
};float Rectangle::height;   		// 需要在全局处声明,可以初始化,也可以不初始化(默认为0)int main()
{Rectangle::show();          //  1、4 ①cout << Rectangle::height << endl;Rectangle rec1;rec1.height = 80;Rectangle rec2;rec2.show();                    // 4 ②cout << rec2.height << endl;cout << "rec1.height\t" << &(rec1.height) << endl;cout << "rec2.height\t" << &(rec2.height) << endl;cout << sizeof(Rectangle) << endl;return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;class Test
{
public:void func0(){cout << "void func0()" << endl;func1();}// 静态成员函数static void func1(){cout << "static void func1()"<< endl;func2();}static void func2(){cout << "static void func2()" << endl;
//        func0(); 		// 6.错误,静态成员函数,不能调用非静态成员函数}
};int main()
{// 类名直接调用Test::func1();Test t1;t1.func0();t1.func2();return 0;
}

在这里插入图片描述

如果要在静态成员函数内调用非静态成员的属性方法,可以通过参数将对象传进来。

#include <iostream>
using namespace std;class Test
{
public:void func0(){cout << "void func0()" << endl;
//        func1();}// 静态成员函数static void func1(Test &t){t.func0();cout << "static void func1(Test &t)"<< endl;func2();}static void func2(){cout << "static void func2()" << endl;
//        func0(); // 错误,静态成员函数,不能调用非静态成员函数}
};int main()
{// 类名直接调用
//    Test::func1();Test t1;t1.func0();t1.func1(t1);t1.func2();return 0;
}

在这里插入图片描述

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

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

相关文章

3D点云目标检测:VoxelNex解读

VoxelNext 通用检测器 vs VoxelNext一、3D稀疏卷积模块1.1、额外的两次下采样消融实验结果代码 1.2、稀疏体素删减消融实验&#xff1a;代码 二、稀疏体素高度压缩代码 三、稀疏预测head 通用检测器 vs VoxelNext 一、3D稀疏卷积模块 1.1、额外的两次下采样 使用通用的3D spa…

保姆级大猿人中控系统搭建教程/话费充值系统/支持代理分销(12.1更新下)

前言 csdn上面是多久没更新啦&#xff0c;没啥值得写的&#xff0c;有也懒得写~ 写文章依然是那么飘逸&#xff0c;哈哈~ 最近看上了一款系统还是挺不错的&#xff0c;直接对接话费、垫费、燃气api充值的系统&#xff0c;还可以&#xff0c;市面上主流的话费中控系统&#xf…

最新发布SPAB模块,YOLOv5改进之SPAB

目录 一、原理 二、代码 三、应用到YOLOv5 一、原理 单幅图像超分辨率(SISR)是低分辨率计算机视觉中的一项重要任务,旨在从低分辨率图像中重建高分辨率图像。传统的注意机制虽然显著提高了SISR的性能,但往往导致网络结构复杂、参数过多,导致推理速度慢

python爬虫基础知识

使用python进行网络爬虫开发之前&#xff0c;我们要对什么是浏览器、什么HTML&#xff0c;HTML构成。请求URL的方法都有一个大概了解才能更清晰的了解如何进行数据爬取。 什么是浏览器&#xff1f; 网页浏览器&#xff0c;简称为浏览器,是一种用于检索并展示万维网信息资源的…

【力扣 面试题02.07链表相交】一种思路极其清晰的解法

力扣一单简单题&#xff0c;看完大佬的题解真是佩服得五体投地&#xff01; 虽是一道简单题&#xff0c;当我吭哧吭哧写了几十行后&#xff0c;看到大佬仅仅几行直接秒掉&#xff0c;只能说算法的本质还是数学&#xff0c;数学逻辑思维真是太重要了&#xff0c;有时候真得慢慢去…

操作系统进程与线程篇

目录 一、进程 1.1、进程状态 1.2、进程的控制结构 1.3、进程的控制 1.4、进程的上下文切换 二、线程 2.1.线程是什么 2.2、线程与进程的比较 2.3、线程的上下文切换 2.4、线程的实现 2.5、轻量级线程 三、进程间的通信方式 3.1、管道 3.2、消息队列 3.3、共享内…

代码人生,养生有道

导言&#xff1a; 在代码的征程中&#xff0c;我们往往忽略了自身的身体健康。这次的活动&#xff0c;我们不妨一同探讨一下&#xff0c;作为程序员&#xff0c;如何通过科学的养生方式&#xff0c;告别亚健康&#xff0c;迎接更健康、更充实的人生。 工作中的挑战 繁忙的工…

Node.js与npm的准备与操作

1.下载 Node.js官网&#xff1a;Node.jsNode.js is a JavaScript runtime built on Chromes V8 JavaScript engine.https://nodejs.org/en 打开后的界面如下&#xff1a; LTS&#xff08;Long Term Support&#xff09;&#xff1a;长期支持版&#xff0c;稳定版 Current&am…

键入网址到网页显示,期间发生了什么?(计算机网络)

一、浏览器首先会对URL进行解析 下面以http://www.server.com/dir1/file1.html为例 当没有路径名时&#xff0c;就代表访问根目录下事先设置的默认文件&#xff0c;也就是 /index.html 或者 /default.html 对URL进行解析之后&#xff0c;浏览器确定了 Web 服务器和文件名&am…

算法——动态规划

动态规划&#xff1a;有很多重叠子问题&#xff0c;每一个状态一定是由上一个状态推导出来的 贪心&#xff1a;没有状态推导&#xff0c;而是从局部直接选最优的 动规五步曲&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 确定递推公式&#xff08;容…

分享89个清新唯美PPT,总有一款适合您

分享89个清新唯美PPT&#xff0c;总有一款适合您 89个清新唯美PPT下载链接&#xff1a;https://pan.baidu.com/s/14DAA9jvVmlQZ_FJ4DNy9Rw?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整…

Java Class文件结构细节最全解读

官方文档位置&#xff1a;https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html Class 类的本质 任何一个Class文件都对应着唯一一个类或接口的定义信息&#xff0c;但反过来说&#xff0c;Class文件实际上它并不一定以磁盘文件的形式存在。Class 文件是一组以8位字…

解决电脑蓝屏问题:SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,回到系统还原点

解决电脑蓝屏问题&#xff1a;SYSTEM_THREAD_EXCEPTION_NOT_HANDLED&#xff0c;回到系统还原点 1&#xff0c;蓝屏显示问题1.1&#xff0c;蓝屏1&#xff0c;清楚显示1.2&#xff0c;蓝屏2&#xff0c;模糊显示 2&#xff0c;排除故障问题3&#xff0c;解决蓝屏的有效方法 1&a…

Mac电脑音乐标签管理 Yate 激活最新 for Mac

Yate是一款非常实用的音频编辑和标记软件&#xff0c;它提供了丰富的功能和工具来帮助用户编辑、整理和管理音频文件。无论是在音乐收藏管理、DJ和音乐制作方面&#xff0c;还是在其他需要处理大量音频文件的领域&#xff0c;Yate都是非常值得推荐的工具。 Yate for Mac功能特…

(2)(2.2) Lightware SF45/B(350度)

文章目录 前言 1 安装SF45/B 2 连接自动驾驶仪 3 通过地面站进行配置 4 参数说明 前言 Lightware SF45/B 激光雷达(Lightware SF45/B lidar)是一种小型扫描激光雷达&#xff08;重约 50g&#xff09;&#xff0c;扫描度可达 350 度&#xff0c;扫描范围 50m。 1 安装SF45…

安全风险综合监测预警平台建设指南(2023 版)》正式发布,汉威科技方案领跑行业

11月24日&#xff0c;国务院安委会办公室印发《城市安全风险综合监测预警平台建设指南&#xff08;2023版&#xff09;》&#xff08;以下简称“指南”&#xff09;&#xff0c;引发行业密切关注。 据悉&#xff0c;“指南”在总结前期18 个试点城市&#xff08;区&#xff09;…

一文回顾 Polkadot 跨链技术演进,了解 Polkadot 2.0 的未来

Polkadot 的起源、完善和上线过程经历了怎样的技术迭新与路线升级&#xff1f;深入把握 Polkadot 技术模型与生态合约才能让我们更好地深耕 Polkadot 生态。 11 月 25 日晚上&#xff0c;Substrate Saturday 第 19 期活动如期举行&#xff0c;Parity 工程师 Suvi Dong、Kaicha…

MJPG-streamer方案实现物联网视频监控

目录 前言 一、JPEG&#xff0c;MJPG格式简介 JPEG MJPG MJPG的优点 MJPG的缺点 二、软硬件准备 三、编译MJPG-streamer 四、运行MJPG-streamer 五、其它常见用法 六、MJPG-streamer 程序框架 七、源码下载 前言 最近想做一个安防相关的项目&#xff0c;所以跟着韦…

RubyMine 2023 年下载、安装、使用教程,详细图解

大家好&#xff0c;今天为大家带来的是RubyMine 2023 年下载、安装、使用教程&#xff0c;详细图解。 文章目录 1 RubyMine 简介2 RubyMine 下载、安装教程RubyMine 下载RubyMine 安装 3 RubyMine 汉化4. 常用快捷键一级必会二级进阶 1 RubyMine 简介 RubyMine 是一个为 Ruby …

什么是企业资金

我从两个方面来诠释企业资金管理&#xff1a; 1、企业资金管理是什么&#xff1f; 2、企业资金管理包括什么&#xff1f; 一、企业资金管理是什么&#xff1f; 众所周知&#xff0c;每个企业都有对应的财务部门&#xff0c;专门负责管理企业的“钱”&#xff0c;和企业的“帐…