类 —— 友元、常/静态成员函数
类
类的大小
和结构体大小求法一致。但需注意,普通空类也会占用 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;
}
相关文章:

类 —— 友元、常/静态成员函数
类 类的大小 和结构体大小求法一致。但需注意,普通空类也会占用 1 字节大小,因为普通空类可以实例化对象。 而 抽象空类占 4 字节(32 位机中),因为抽象空类中含有虚指针(含有虚函数的非抽象空类同理&am…...
单页面应用
单页面应用 1.什么是SPA 多页面应用:每个页面都是独立的html文件,页面切换是整体刷新,需要重新加载html、css、JS等文件,容易实现搜索引擎,数据通过url、cookie、localStore传递。 单页面应用:多个页面是…...

六、ZooKeeper Java API操作
目录 1、引入maven坐标 2、节点的操作 这里操作Zookeeper的JavaAPI使用的是一套zookeeper客户端框架 Curator ,解决了很多Zookeeper客户端非常底层的细节开发工作 。 Curator包含了几个包:...

美甲美睫店预约会员管理小程序作用如何
美甲美睫是美业中较为重要的类目,主要以小摊、门店/连锁形式,随着线上化程度加深,传统线下美业店面临着困境,想要进一步增长及解决痛点,就需要线上数字化运营得到更多生意。 那么通过【雨科】平台搭建美甲美睫店小程序…...
Vue3为什么会推出组合式API
前言 大学前端入门学的vue2,工作了又用的React,现在想学习一下Vue3,开篇就介绍了组合式API,这和我认知里的vue2的选项式API区别还是蛮大的。本篇文章简单介绍一下组合式API。 什么是组合式API Vue 3引入了一种新的 API…...

windows11 phpstudy_pro php8.2 安装redis扩展
环境:windows11 phpstudy_pro php8.2.9 一、命令查看是否安装redis扩展 在对应网站中通过打开,,选择对应的PHP版本,用命令 php -m 查看自己的php 有没有redis扩展 上面如果有,说明已经安装了,如果没有安装࿱…...

中英双语大模型ChatGLM论文阅读笔记
论文传送门: [1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling [2] Glm-130b: An open bilingual pre-trained model Github链接: THUDM/ChatGLM-6B 目录 笔记AbstractIntroductionThe design choices of GLM-130BThe …...

力扣题:字符串的反转-11.24
力扣题-11.24 [力扣刷题攻略] Re:从零开始的力扣刷题生活 力扣题1:151. 翻转字符串里的单词 解题思想:保存字符串中的单词即可 class Solution(object):def reverseWords(self, s):""":type s: str:rtype: str"&quo…...

NIO--07--Java lO模型详解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 何为 IO?先从计算机结构的角度来解读一下I/o.再从应用程序的角度来解读一下I/O 阻塞/非阻塞/同步/异步IO阻塞IO非阻塞IO异步IO举例 Java中3种常见的IO模型BIO (Blo…...

OpenSSH 漏洞修复升级最新版本
Centos7系统ssh默认版本一般是OpenSSH7.4左右,低版本是有漏洞的而且是高危漏洞,在软件交付和安全扫描上是过不了关的,一般情况需要升级OpenSSH的最新版本 今天详细说下升级最新版本的处理过程(认真看会发现操作很简单,…...
【数据结构和算法】无限集中的最小数字
其他系列文章导航 Java基础合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 三、代码 四、总结 前言 这是力扣的2336题,难度为中等,解题方案有很多种,本文讲解我认为…...

SimpleDataFormat 非线程安全
目录 前言 正文 1.出现异常 2.解决方法1 3.解决方法2 总结 前言 SimpleDateFormat 类是 Java 中处理日期和时间格式化和解析的类,但它并不是线程安全的。这意味着多个线程不能安全地共享一个 SimpleDateFormat 实例进行日期和时间的解析和格式化。当多个…...

SpringBoot : ch12 多模块配置YAML文件
前言 当您使用SpringBoot框架进行项目开发时,通常需要配置一些参数和属性。在实际开发中,可能需要将这些配置参数分成多个不同的YAML文件,并将它们组织到不同的模块中。这样可以方便管理和维护配置文件,并且可以避免配置文件的冲…...

TensorRT之LeNet5部署(onnx方式)
文章目录 前言LeNet-5部署1.ONNX文件导出2.TensorRT构建阶段(TensorRT模型文件)🧁创建Builder🍧创建Network🍭使用onnxparser构建网络🍬优化网络🍡序列化模型🍩释放资源 3.TensorRT运行时阶段(推理)&#x…...

Xilinx FPGA平台DDR3设计详解(二):DDR SDRAM组成与工作过程
本文主要介绍一下DDR SDRAM的基本组成以及工作过程,方便大家更好的理解和掌握DDR的控制与读写。 一、DDR SDRAM的基本组成 1、SDRAM的基本单元 SDRAM的基本单元是一个CMOS晶体管和一个电容组成的电路。 晶体管最上面的一端,称作栅极,通过…...
ios(swiftui) 属性包装器详解
目录 1. State 2. Binding 3. ObservedObject 和Published 4. StateObject 5. EnvironmentObject和Environment 6. AppStorage 在 SwiftUI 中,属性包装器用于增强和管理视图的状态,以及处理视图与数据模型之间的绑定和交互。下面是一些常见…...
【智能家居】面向对象编程OOP和设计模式(工厂模式)
面向对象编程 类和对象 面向对象编程和面向过程编程区别 设计模式 软件设计模式按类型分 工厂模式 面向对象编程 面向对象编程(Object-Oriented Programming,OOP)是一种程序设计范式,其中程序被组织成对象的集合,每…...
Docker安装Memcached+Python调用
简介:Memcached是一个通用的分布式内存缓存系统。它通常用于通过在RAM中缓存数据和对象来加速动态数据库驱动的网站,以减少必须读取外部数据源(如数据库或API)的次数。Memcached的API提供了一个分布在多台机器上的非常大的哈希表。…...
网页开发 HTML
目录 HTML概述 HTML结构 HTML标签语法 基本标签 标题标签 换行标签 段落标签 文本格式化标签 特殊符号 div和span标签 超链接标签 锚点 img标签 列表标签 表格标签 表单标签 HTML概述 HTML,即超文本标记语言(HyperText Markup Language …...

SHAP(五):使用 XGBoost 进行人口普查收入分类
SHAP(五):使用 XGBoost 进行人口普查收入分类 本笔记本演示了如何使用 XGBoost 预测个人年收入超过 5 万美元的概率。 它使用标准 UCI 成人收入数据集。 要下载此笔记本的副本,请访问 github。 XGBoost 等梯度增强机方法对于具有…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...