当前位置: 首页 > news >正文

C++共享数据的保护

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

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()constb.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()constb.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;//errorreturn 0;
}

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

相关文章:

C++共享数据的保护

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

MyBatisPlus学习记录

MyBatisPlus(简称MP&#xff09;是基于MyBatis框架基础上开发的增强型工具&#xff0c;旨在简化开发、提高效率 MyBatisPlus简介 入门案例 创建新模块&#xff0c;选择Spring初始化&#xff0c;并配置模块相关基础信息选择当前模块需要使用的技术集&#xff08;仅选择MySQL …...

如何开启一个java微服务工程

安装idea IDEA常用配置和插件&#xff08;包括导入导出&#xff09; https://blog.csdn.net/qq_38586496/article/details/109382560安装配置maven 导入source创建项目 修改项目编码utf-8 File->Settings->Editor->File Encodings 修改项目的jdk maven import引入…...

libhv之hio_t分析

上一篇文章解析了fd是怎么与io模型关联。其中最主要的角色扮演者&#xff1a;hio_t 1. hio_t与hloop的关系 fd的值即hio_t所在loop ios变量中所在的index值。 hio_t ios[fd] struct hloop_s { ...// ios: with fd as array.index//io_array保存了和hloop关联的所有hio_t&…...

C语言的转义字符

转义字符也叫转移序列&#xff0c;包含如下&#xff1a; 转移序列 \0oo 和 \xhh 是 ASCII 码的特殊表示。 八进制数示例&#xff1a; 代码&#xff1a; #include<stdio.h> int main(void) {char beep\007;printf("%c\n",beep);return 0; }结果&#xff1a; …...

【腾讯云 Cloud Studio 实战训练营】CloudStudio体验真正的现代化开发方式,双手插兜不知道什么叫对手!

CloudStudio体验真正的现代化开发方式&#xff0c;双手插兜不知道什么叫对手&#xff01; 文章目录 CloudStudio体验真正的现代化开发方式&#xff0c;双手插兜不知道什么叫对手&#xff01;前言出现的背景一、CloudStudio 是什么&#xff1f;二、CloudStudio 的特点三、CloudS…...

Pandas时序数据分析实践—时序数据集

1. 跑步运动为例&#xff0c;对运动进行时序分析 时序数据是指时间序列数据&#xff0c;是按照时间顺序排列的数据集合&#xff0c;每个数据点都与一个特定的时间戳相关联。在跑步活动中&#xff0c;我们可以将每次跑步的数据记录作为一个时序数据样本&#xff0c;每个样本都包…...

use strict 是什么意思?使用它区别是什么?

use strict 是什么意思&#xff1f;使用它区别是什么&#xff1f; use strict 代表开启严格模式&#xff0c;这种模式下使得 JavaScript 在更严格的条件下运行&#xff0c;实行更严格解析和错误处理。 开启“严格模式”的优点&#xff1a; 消除 JavaScript 语法的一些不合理…...

常见OOM异常分析排查

常见OOM异常分析排查 Java内存溢出Java堆溢出原因解决思路总结 Java内存溢出 java堆用于存储对象实例,如果不断地创建对象,并且保证GC Root到对象之间有可达路径,垃圾回收机制就不会清理这些对象,对象数量达到最大堆的容量限制后就会产生内存溢出异常. Java堆溢出原因 无法在…...

kubernetes网络之网络策略-Network Policies

Kubernetes 中&#xff0c;Network Policy&#xff08;网络策略&#xff09;定义了一组 Pod 是否允许相互通信&#xff0c;或者与网络中的其他端点 endpoint 通信。 NetworkPolicy 对象使用标签选择Pod&#xff0c;并定义规则指定选中的Pod可以执行什么样的网络通信&#xff0…...

交换机VLAN技术和实验(eNSP)

目录 一&#xff0c;交换机的演变 1.1&#xff0c;最小网络单元 1.2&#xff0c;中继器&#xff08;物理层&#xff09; 1.3&#xff0c;集线器&#xff08;物理层&#xff09; 1.4&#xff0c;网桥&#xff08;数据链路层&#xff09; 二&#xff0c;交换机的工作行为 2.…...

8.Winform界面打包成DLL提供给其他的项目使用

背景 希望集成一个Winform的框架&#xff0c;提供权限菜单&#xff0c;根据权限出现各个Winform子系统的菜单界面。不希望把所有的界面都放放在同一个解决方案下面。用各个子系统建立不同的解决方案&#xff0c;建立代码仓库&#xff0c;进行管理。 实现方式 将Winform的UI界…...

海量数据存储组件Hbase

hdfs hbase NoSQL数据库 支持海量数据的增删改查 基于Rowkey查询效率特别高 kudu 介于hdfs和hbase之间 hbase依赖hadoopzookeeper&#xff0c;同时整合框架phoenix(擅长读写),hive&#xff08;分析数据&#xff09; k&#xff0c;v 储存结构 稀疏的&#xff08;为空的不存…...

(一)基于Spring Reactor框架响应式异步编程|道法术器

在执行程序时: 通常为了提供性能,处理器和编译器常常会对指令进行重排序。 从排序分为编译器重排序和处理器重排序两种 * (1)编译器重排序: 编译器保证不改变单线程执行结构的前提下,可以调整多线程语句执行顺序&#xff1b; * (2)处理器重排序&#xff1a; 如果不存在数据依赖…...

Vue3 让localstorage变响应式

Hook使用方式&#xff1a; import {useLocalStore} from "../js/hooks"const aauseLocalStore("aa",1) 需求一&#xff1a; 通过window.localStorage.setItem可以更改本地存储是&#xff0c;还可以更新aa的值 window.localStorage.setItem("aa&quo…...

【深度学习】InST,Inversion-Based Style Transfer with Diffusion Models,论文,风格迁移,实战

代码&#xff1a;https://github.com/zyxElsa/InST 论文&#xff1a;https://arxiv.org/abs/2211.13203 文章目录 AbstractIntroductionRelated WorkImage style transferText-to-image synthesisInversion of diffusion models MethodOverview ExperimentsComparison with Sty…...

【CSS】3D卡片效果

效果 index.html <!DOCTYPE html> <html><head><title> Document </title><link type"text/css" rel"styleSheet" href"index.css" /></head><body><div class"card"><img…...

OrderApplication

目录 1 OrderApplication 2 /// 查询订单 2.1.1 //补充商品单位 2.1.2 //补充门店名称 2.1.3 //补充门店名称 2.1.4 //订单售后 2.1.5 //订单项售后 OrderApplication...

如何在保健品行业运用IPD?

保健品是指能调节机体功能&#xff0c;不以治疗为目的&#xff0c;并且对人体不产生任何急性、亚急性或者慢性危害的产品。保健品是食品的一个种类&#xff0c;具有一般食品的共性&#xff0c;其含有一定量的功效成分&#xff0c;能调节人体的机能&#xff0c;具有特定的功效&a…...

Flink系列之:动态发现新增分区

Flink系列之&#xff1a;动态发现新增分区 一、动态发现新增分区二、Flink SQL动态发现新增分区三、Flink API动态发现新增分区 为了在不重新启动 Flink 作业的情况下处理主题扩展或主题创建等场景&#xff0c;可以将 Kafka 源配置为在提供的主题分区订阅模式下定期发现新分区。…...

eclipse版本与jdk版本对应关系

官网&#xff1a;Eclipse/Installation - Eclipsepedia eclipse历史版本&#xff08;2007-&#xff09;&#xff1a;Older Versions Of Eclipse - Eclipsepedia Eclipse Packaging Project (EPP) Releases | Eclipse Packages...

File类的学习

java.io.File类 文件和目录路径的抽象表达形式是一个与操作系统无关的类&#xff0c;任何一个操作系统都可以使用这个类中的方法 File.pathSeparator 文件路径分隔符&#xff0c;windows是分号&#xff0c;linux是&#xff1a; File.separator 文件名分隔符&#xff0c;window…...

Linux 操作系统 Red Hat Enterprise Linux 安装教程

文章目录 笔者的操作环境&#xff1a; 制作环境&#xff1a; Win32 Disk Imager 1.0.0 Windows 10 教育版 ISO&#xff1a; Red Hat Enterprise Linux 9.2 x86_64 Red Hat Enterprise Linux&#xff08;RHEL&#xff09;是一种 Linux 操作系统。安装此操作系统的难题在于&a…...

关于拓扑排序

又重新学了一下拓扑排序&#xff0c;这次发现就十分简单了&#xff0c;拓扑排序的步骤 1.他必须是一个有向无环图&#xff0c;起点我们就是入度为0的点 2.我们首先要输出的就是入度为0的点&#xff0c;然后依次删除这些点连向的点&#xff0c;使这些点的入度-1&#xff0c;如果…...

【C++】开源:Boost库常用组件配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Boost库常用组件配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…...

用python通过http实现文件传输,分为发送端和接收端

要使用Python通过HTTP实现文件传输&#xff0c;可以使用Python的 requests 库来发送和接收HTTP请求。以下是一个示例代码&#xff0c;其中包括发送端和接收端的实现。 发送端&#xff1a; import requestsdef send_file(file_path, url):with open(file_path, rb) as file:re…...

数据结构--图的遍历 DFS

数据结构–图的遍历 DFS 树的深度优先遍历 //树的先根遍历 void PreOrder(TreeNode *R) {if(R ! NULL){visit(R); //访问根节点while(R还有下一个子树T)PreOrder(T);//先根遍历下一棵子树} }图的深度优先遍历 bool visited [MAX_VERTEX_NUM]; //访问标记数组 void DFS(Grap…...

SpringBoot集成MyBatisPlus+MySQL(超详细)

前言 查看此文章前强烈建议先看这篇文章&#xff1a;Java江湖路 | 专栏目录 该文章纪录的是SpringBoot快速集成MyBatis Plus&#xff0c;每一步都有记录&#xff0c;争取每一位看该文章的小伙伴都能操作成功。达到自己想要的效果~ 文章目录 前言1、什么是MyBatisPlus2、Spring…...

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

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

解决外部主机无法访问Docker容器的方法

使用Docker启动了一个tomcat容器&#xff0c;并做了端口映射&#xff0c;但是外部主机仍然无法访问。 编辑centos上的配置文件 vi /etc/sysctl.conf net.ipv4.ip_forward1 systemctl restart network保存以后即可生效&#xff0c;这个配置是开启linux的ip数据包转发功能&#…...