当前位置: 首页 > 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函数并不是以常引用的方式进行传参,但是,因为主函数中定义的对象为常对象,不可以采用普通的引用进行传参,所以编译不会通过。

http://www.lryc.cn/news/106339.html

相关文章:

  • MyBatisPlus学习记录
  • 如何开启一个java微服务工程
  • libhv之hio_t分析
  • C语言的转义字符
  • 【腾讯云 Cloud Studio 实战训练营】CloudStudio体验真正的现代化开发方式,双手插兜不知道什么叫对手!
  • Pandas时序数据分析实践—时序数据集
  • use strict 是什么意思?使用它区别是什么?
  • 常见OOM异常分析排查
  • kubernetes网络之网络策略-Network Policies
  • 交换机VLAN技术和实验(eNSP)
  • 8.Winform界面打包成DLL提供给其他的项目使用
  • 海量数据存储组件Hbase
  • (一)基于Spring Reactor框架响应式异步编程|道法术器
  • Vue3 让localstorage变响应式
  • 【深度学习】InST,Inversion-Based Style Transfer with Diffusion Models,论文,风格迁移,实战
  • 【CSS】3D卡片效果
  • OrderApplication
  • 如何在保健品行业运用IPD?
  • Flink系列之:动态发现新增分区
  • eclipse版本与jdk版本对应关系
  • File类的学习
  • Linux 操作系统 Red Hat Enterprise Linux 安装教程
  • 关于拓扑排序
  • 【C++】开源:Boost库常用组件配置使用
  • 用python通过http实现文件传输,分为发送端和接收端
  • 数据结构--图的遍历 DFS
  • SpringBoot集成MyBatisPlus+MySQL(超详细)
  • 一边是计算机就业哀鸿遍野,一边是高考生疯狂涌向计算机专业
  • 解决外部主机无法访问Docker容器的方法
  • IDEA中修改类头的文档注释信息