C++共享数据的保护

news2024/11/19 6:24:35

虽然数据隐藏保护了数据的安全性,但各种形式的数据共享却又不同程度地破坏了数据的安全。因此,对于既需要共享有需要防止改变的数据应该声明为常量。因为常量在程序运行期间不可改变,所以可以有效保护数据。

1.常对象

常对象:它的数据成员值在对象的整个生存期内不能被改变。也就是说,常对象必须进行初始化,而且不能被更新。

声明常对象的语法形式:

const 类型说明符 对象名;

例如:

class A
{
public:
	A(int i,int j):x(i),y(j){}
private:
	int x, y;
};
const A a(3, 4);//a是常对象,不能被更新

与基本数据类型的常量相似,常对象的值不能被改变。在C++语法中,对基本数据类型的常量提供了可靠的保护。如果程序中出现了类似下面这样的语句,编译时是会出错的。也就是说,语法检查时确保了常量不能被赋值。

const int n=10;//正确,用10对常量n进行初始化
n=20;//错误,不能对常量赋值

【注意】在定义一个变量或者常量时为它指定初值叫做初始化,而在定义一个变量或常量以后使用赋值运算符修改它的值叫做赋值。

语法保障类类型的常对象的值不被改变:
改变对象的数据成员值有两个途径,一是通过对象名访问其成员对象,由于常对象的数据成员都被视为常量,这时语法限制不能赋值。二是在类的成员函数中改变数据成员的值,然而几乎无法预料和统计哪些成员函数会改变数据成员的值,对此语法只好规定不能通过常对象调用普通的成员函数。因此,为常对象定义了常成员函数。

【注意】基本数据类型的常量也可以看作一种特殊的常对象。

2.常成员函数

使用关键字const修饰的函数称为常成员函数,常成员函数的声明格式如下:

类型说明符 函数名(参数表)const;

【注意】
(1)const是函数类型的一个组成部分,因此在函数的定义部分也要带const关键字。
(2)如果将一个对象说明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数(这就是C++从语法机制上对常对象的保护,也是常对象唯一的对外接口方式)。
(3)无论是否通过常对象调用常成员函数,在常成员函数调用期间,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能针对目的对象调用该类中没有用const修饰的成员函数(这就保证了在常成员函数中不会更改目的对象的数据成员值)。
(4)const关键字可以用于对重载函数的区分。例如,如果在类中这样声明:

void print();
void print() const;

这是对print函数的有效重载。
【注意】如果仅以const关键字区分对成员函数的重载,那么通过非const的对象调用该函数,两个重载函数都可以与之匹配,这是编译器将选择最近的函数重载——不带const关键字的函数。

【例】在R类中声明了一个常成员函数。

class R
{
public:
	R(int r1, int r2) :r1(r1), r2(r2) {}
	void print();
	void print()const;
private:
	int r1, r2;
};
void R::print()
{
	cout << r1 << ":" << r2 << endl;
}
void R::print()const
{
	cout << r1 << ";" << r2 << endl;
}
int main()
{
	R a(5, 4);
	a.print();//调用void print()
	const R b(20, 32);//调用void print()const
	b.print();
	return 0;
}

运行结果及分析:
在这里插入图片描述
在R类中声明了两个同名函数print,其中一个是常成员函数。在主函数中定义了两个对象a和b,其中a是普通的R类类型的对象,b是R类类型的常对象。通过对象a调用的是没有const修饰的print函数,而通过对象b调用的是用const修饰的print常成员函数。

(1)如果将程序做以下修改:

class R
{
public:
	R(int r1, int r2) :r1(r1), r2(r2) {}
	void print();
	//void print()const;
private:
	int r1, r2;
};
void R::print()
{
	cout << r1 << ":" << r2 << endl;
}
//void R::print()const
//{
//	cout << r1 << ";" << r2 << endl;
//}
int main()
{
	R a(5, 4);
	a.print();//调用void print()
	const R b(20, 32);//调用void print()const
	b.print();//错误
	return 0;
}

这样会导致编译不通过,因为b在主函数中被定义为常对象,常对象不可以调用普通的成员函数。
(2)如果将程序做以下修改:

class R
{
public:
	R (int r1,int r2):r1(r1),r2(r2){}
	//void print();
	void print()const;
private:
	int r1, r2;
};
//void R::print()
//{
//	cout << r1 << ":" << r2 << endl;
//}
void R::print()const
{
	cout << r1 << ";" << r2 << endl;
}
int main()
{
	R a(5, 4);
	a.print();
	const R b(20, 32);
	b.print();
	return 0;
}

运行结果及分析:
在这里插入图片描述
如果R类中没有普通成员函数print,只有const修饰的常成员函数void print()const;,那么在主函数中,普通的R类类型的对象a和常对象b都会调用常成员函数,所以普通对象和常对象都可以调用常成员函数。

3.常数据成员

就像一般数据一样,类的数据成员也可以是常量,使用const说明的数据成员为常数据成员。如果在一个类中说明了常数据成员,那么任何函数中都不能对该成员赋值。构造函数对常数据成员进行初始化,就只能通过初始化列表。

【例】在类A中声明了一个常数据成员。

class A
{
public:
	A(int i);
	void print();
private:
	const int a;
	static const int b;//静态常数据成员
};

const int A::b = 10;//静态常数据成员在类外声明和初始化。

//常数据成员只能通过初始化列表来获得初值
A::A(int i) :a(i){}

void A::print()
{
	cout << a << ":" << b << endl;
}
int main()
{
	/*建立对象a和b,并以100和1作为初值,分别调用构造函数,
	通过构造函数的初始化列表给对象的常数据成员赋值*/
	A m(100);
	A n(1);
	m.print();
	n.print();
	return 0;
}

运行结果:
在这里插入图片描述
分析:

类数据成员中的静态变量和常量都应当在类定义之外加以定义,但C++标准规定了一个例外:类的静态常量如果具有整型或枚举类型,那么可以直接在类中定义为它指定常量值,例如上面代码可以改写为如下所示,在类中定义时直接给b指定常量值10:

class A
{
public:
	A(int i);
	void print();
private:
	const int a;
	static const int b=10;//静态常数据成员
};

//const int A::b = 10;//静态常数据成员在类外声明和初始化。

//常数据成员只能通过初始化列表来获得初值
A::A(int i) :a(i){}

void A::print()
{
	cout << a << ":" << b << endl;
}
int main()
{
	/*建立对象a和b,并以100和1作为初值,分别调用构造函数,
	通过构造函数的初始化列表给对象的常数据成员赋值*/
	A m(100);
	A n(1);
	m.print();
	n.print();
	return 0;
}

这时不必在类外定义A::b,因为编译器会将程序中对A::b的所有引用都替换成数值10,一般无须再为A::b分配空间。但也有例外,例如如果程序中出现了对b取地址的情况,则必须通过专门的定义为A::b分配空间。由于已经在类中为它指定了初值,就不能再在类定义之外为它指定初值,即使两处给的值一样也不行。

但是对于类数据成员中的常量,必须在构造函数的初始化列表对其进行赋初值,即初始化。

4.常引用

如果在声明引用时用const修饰,被声明的引用就是常引用常引用所引用的对象不能被更新。如果常引用作为形参,便不会意外地发生对实参的更改。常引用的声明形式如下:

const 类型说明符 &引用名;

非const的引用只能绑定到普通的对象,而不能绑定到常对象,但常引用可以绑定到常对象。一个常引用,无论绑定到一个普通对象,还是一个常对象,通过该引用访问该对象时,都只能把该对象当作常对象。这意味着,对于基本数据类型的引用,则不能为数据赋值,对于类类型的引用,则不能修改它的数据成员,也不能调用它的非const成员函数。

【例】常引用做形参

class Point//Point类的定义
{
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) {}
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(const Point& p1, const Point& p2);//友元函数声明
private://私有数据成员
	int x, y;
};

float dist(const Point& p1, const Point& p2)//常引用作为形参
{
	double  x = p1.x - p2.x;//通过对象访问Point类的私有数据成员
	double  y = p1.y - p2.y;
	return static_cast<float>(sqrt(x * x + y * y));
}

int main()//主函数
{
	const Point myp1(1, 1), myp2(4, 5);//定义Point类的对象
	cout << "两点之间的距离为:";
	cout << dist(myp1, myp2) << endl;//计算两点之间的距离
	return 0;
}

运行结果及分析:

在这里插入图片描述
由于dist函数中,无须修改两个传入对象的值,因此将传参方式改为传递常引用更合适,这样,调用dist函数时,就可以用常对象作为参数。

【注意】对于在函数中无须改变其值的参数,不宜使用普通引方式传递,因为那会使得对象无法被传入,采用传值方式或传递常引用方式可避免这一问题。对于大对象来说,传值耗时较多,因此传递常引用为宜。拷贝构造函数的参数一般也宜采用常引用传递。
例如将上述代码改写为:

class Point//Point类的定义
{
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) {}
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(Point& p1, Point& p2);//友元函数声明
private://私有数据成员
	int x, y;
};

float dist(Point& p1, Point& p2)//常引用作为形参
{
	double  x = p1.x - p2.x;//通过对象访问Point类的私有数据成员
	double  y = p1.y - p2.y;
	return static_cast<float>(sqrt(x * x + y * y));
}

int main()//主函数
{
	const Point myp1(1, 1), myp2(4, 5);//定义Point类的对象
	cout << "两点之间的距离为:";
	cout << dist(myp1, myp2) << endl;//error
	return 0;
}

此时,dist函数并不是以常引用的方式进行传参,但是,因为主函数中定义的对象为常对象,不可以采用普通的引用进行传参,所以编译不会通过。

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

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

相关文章

数模国赛、华为杯竞赛火爆来袭~想大展身手?快来看看~

友友们好&#xff5e; 华为杯竞赛和数学建模国赛即将拉开帷幕&#xff0c;也许你对这激烈的竞争感到有些焦虑&#xff0c;但是请别担心&#xff01;~知名团队将为你提供全方位的帮助&#xff0c;让你在比赛中游刃有余&#xff0c;一飞冲天&#xff01; 这个团队在这方面经验丰…

【雕爷学编程】Arduino动手做(180)---Seeeduino Lotus开发板2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

互联网宠物医院系统开发:数字化时代下宠物医疗的革新之路

随着人们对宠物关爱意识的提高&#xff0c;宠物医疗服务的需求也日益增加。传统的宠物医院存在排队等待、预约难、信息不透明等问题&#xff0c;给宠物主人带来了诸多不便。而互联网宠物医院系统的开发&#xff0c;则可以带来许多便利和好处。下面将介绍互联网宠物医院系统开发…

【Vue】兄弟组件值与方法传递使用

Vue 兄弟组件通信 这里使用的是Bus&#xff0c;$bus 思路是在全局挂载一个实例&#xff0c;通过这个实例里的事件派发和事件监听实现跨组件通信&#xff0c;设计模式叫做观察者模式。 例子 A、B组件通信 页面代码 <template><div><!--组件A--><a-compoe…

关于录音怎么转换成文字的小窍门大公开

你是否曾经因为需要记录会议内容、讲座内容或者采访内容而苦恼&#xff0c;手写记录又效率低下&#xff0c;打字又太过麻烦&#xff1f;那么&#xff0c;录音转文字技术或许是你的救星&#xff01;现在&#xff0c;随着技术的不断进步&#xff0c;我们可以通过手机或电脑等设备…

【机密计算-大厂有话说】NVIDIA Hopper H100 上的机密计算

1. 英伟达机密计算路线图(硬件) 在过去的四代中,NVIDIA 一直在不断提高安全性和设备的完整性。最早有文献记载的工作之一是在 NVIDIA V100 GPU 中,为设备上运行的固件提供了 AES 身份验证。身份验证可以保证用户可以信任启动固件没有被破坏,也没有被篡改。随着时…

13年测试经验,性能测试-高并发处理详细,一篇彻底通透...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 涉及抢购、秒杀、…

Python高阶技巧 递归

递归的定义 函数作为一种代码封装&#xff0c;可以被其他程序调用&#xff0c;当然&#xff0c;也可以被函数内部代码调用。这种函数定义中调用函数自身的方式称为递归。 递归的思想 把规模大的问题转化为规模小的、具有与原来问题相同解法的问题来解决。在函数实现时&#…

汽车电子抛负载测试保护选用小封装TVS管 MIN218

汽车24V系统过抛负载测试解决保护方案及电路防护器件选型&#xff0c;东沃电子之前就分享过很多次了。24V电源抛负载瞬态浪涌过压保护经典方案&#xff0c;如下图所示&#xff1a; 从图中可知&#xff0c;24V系统过抛负载测试需要选用TVS管&#xff08;SM8S36CA和6.6SMDJ36CA&a…

【ASP.NET MVC】第一个登录页面(8)

一、准备工作 先从网上&#xff08;站长之家、模板之家&#xff0c;甚至TB&#xff09;下载一个HTML模板&#xff0c;要求一整套的CSS和必要的JS&#xff0c;比如下图&#xff1a; 登录页面的效果是&#xff1a; 首页&#xff1a; 利用这些模板可以减少前台网页的设计——拿来…

【element-ui】form表单初始化页面如何取消自动校验rules

问题描述&#xff1a;elementUI表单提交页面&#xff0c;初始化页面是获取接口数据&#xff0c;给form赋值&#xff0c;但是有时候这些会是空值情况&#xff0c;如果是空值&#xff0c;再给form表单赋值的话&#xff0c;页面初始化时候进行rules校验会不通过&#xff0c;此时前…

【数据结构与算法——TypeScript】数组、栈、队列、链表

【数据结构与算法——TypeScript】 算法(Algorithm)的认识 解决问题的过程中&#xff0c;不仅仅 数据的存储方式会影响效率&#xff0c;算法的优劣也会影响效率 什么是算法&#xff1f; 定义&#xff1a; &#x1f7e2; 一个有限指令集&#xff0c;每条指令的描述不依赖于言语…

一边是计算机就业哀鸿遍野,一边是高考生疯狂涌向计算机专业

在张雪峰推荐的几大专业里&#xff0c;计算机专业是其中之一。近几年&#xff0c;计算机专业报考热度不减&#xff0c;但就业前景却令人堪忧&#xff0c;互联网裁员接二连三&#xff0c;许多码农找不到工作。 一位网友感叹&#xff1a;一边是计算机就业哀鸿遍野&#xff0c;一…

linux系统共享文件夹的创建和使用(VMware )

虚拟机设置共享文件夹 点击设置 点击选项 选择共享文件夹 随便添加一个电脑上的文件夹 虚拟机内打开共享文件夹 打开根目录 打开mnt文件夹 继续点击 最终得到共享文件夹

8Gbps及以上高速信号PCB布线建议

8Gbps及以上高速信号PCB布线建议 —来源&#xff1a;瑞芯微RK3588 PCB设计白皮书 如表1-1所示&#xff0c;RK3588芯片以下接口的信号能工作在8Gbps及以上速率&#xff0c;由于速率很高&#xff0c;PCB布线设计要求会更严格&#xff0c;在“PCBlayout 通用布线规范”的基础上&…

ELK 企业级日志分析系统(ElasticSearch、Logstash 和 Kiabana 详解)

目录 一.ELK简介 1.1ELK的概述 1.2ELK的组成 1.2.1 ElasticSearch 1.2.2 Logstash 1.2.3 Kibana 1.2.4 小总结 1.3可以添加其他组件 1.4filebeat 结合 logstash 带来好处 1.5日志处理的步骤 二.Elasticsearch 2.1Elasticsearch概述 2.2Elasticsearch核心概念 2.2.1接近…

利用尺度因子方法恢复GRACE水储量变化

1.背景 重力恢复与气候实验&#xff08;GRACE&#xff09;观测地球重力势的时间变化。在考虑了大气和海洋效应后&#xff0c;每月到年际尺度上剩余的信号主要与陆地水储存&#xff08;TWS&#xff09;的变化有关。水储存变化的估计受到测量误差和噪声的信号退化影响&#xff0…

windows美化任务栏,不使用软件

1.任务栏透明: 效果图: (1).winr打开命令行 输入regedit回车打开注册表 regedit (2).在注册表中打开 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced 这个路径 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explore…

目标检测与跟踪 (2)- YOLO V8配置与测试

系列文章目录 第一章 目标检测与跟踪 &#xff08;1&#xff09;- 机器人视觉与YOLO V8 目标检测与跟踪 &#xff08;1&#xff09;- 机器人视觉与YOLO V8_Techblog of HaoWANG的博客-CSDN博客3D物体实时检测、三维目标识别、6D位姿估计一直是机器人视觉领域的核心研究课题&a…

python Pandas.rank() 排名函数详解

文章目录 Pandas.rank() 函数详解一、参数解析二、案例分享默认排名降序: ascending Falsemethod minmethod maxmethod firstmethod densena_optionbottompct True Pandas.rank() 函数详解 一、参数解析 method&#xff1a;指定排名时的策略。 默认值为 average&#x…