当前课程知识点:C++语言程序设计基础 >  第5章 数据的共享与保护 >  共享数据的保护 >  共享数据的保护(例5-7)

返回《C++语言程序设计基础》慕课在线视频课程列表

共享数据的保护(例5-7)在线视频

共享数据的保护(例5-7)

共享数据的保护

l  对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)。

l  对于不改变对象状态的成员函数应该声明为常函数

常类型

l常对象:必须进行初始化,不能被更新。

n    const 类名 对象名

l常成员

n    用const进行修饰的类成员:常数据成员和常函数成员

l常引用:被引用的对象不能被更新。

n    const  类型说明符  &引用名

l常数组:数组元素不能被更新(详见第6章)。

n    类型说明符  const  数组名[大小]...

l常指针:指向常量的指针(详见第6章)。

常对象

l用const修饰的对象

l例:

class A

{

  public:

    A(int i,int j) {x=i; y=j;}

                     ...

  private:

    int x,y;

};

A const a(3,4); //a是常对象,不能被更新

l思考:哪些操作有试图改变常对象状态的危险?

常成员

l用const修饰的对象成员

l常成员函数

n   使用const关键字说明的函数。

n   常成员函数不更新对象的数据成员。

n   常成员函数说明格式:

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

这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。

n    const关键字可以被用于参与对重载函数的区分

l通过常对象只能调用它的常成员函数。

l常数据成员

n    使用const说明的数据成员。

例5-7  常成员函数举例

#include<iostream>

using namespace std;

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,52); 

  b.print(); //调用void print() const

       return 0;

}

 

例5-8  常数据成员举例

#include <iostream>

using namespace std;

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() {

//建立对象ab,并以1000作为初值,分别调用构造函数,

//通过构造函数的初始化列表给对象的常数据成员赋初值

  A a1(100), a2(0);

  a1.print();

  a2.print();

  return 0;

}

常引用

l如果在声明引用时用const修饰,被声明的引用就是常引用。

l常引用所引用的对象不能被更新。

l如果用常引用做形参,便不会意外地发生对实参的更改。常引用的声明形式如下:

n    const  类型说明符  &引用名;

 

例5-9  常引用作形参

#include <iostream>

#include <cmath>

using namespace std;

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; 

       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);    

       cout << "The distance is: ";

       cout << dist(myp1, myp2) << endl;

       return 0;

}

 



下一节:多文件结构和预编译命令(例5-10)

返回《C++语言程序设计基础》慕课在线视频列表

共享数据的保护(例5-7)课程教案、知识点、字幕

大家好

欢迎回来继续学习

C++语言程序设计

这一节我们来学习

共享数据的保护

如果我们既需要共享数据

又需要保证被共享的这些数据

不被修改

保证它的安全性

这个时候呢

我们就可以将这些数据

定义为常类型

也就是用const修饰的类型

那么在类的成员函数中呢

有一些函数

它是不会改变对象状态的

那么这样的函数呢

我们又可以把它定义成常函数

也就是

可以用const来修饰这个函数

首先我们来看看

我们可以在程序中

定义哪些常类型

我们可以定义常对象 也就是说在定义对象的时候呢

前面加这样一个const来修饰

常对象就像我们定义变量的时候

定义常量的时候一样

它必须在定义的时候

进行初始化

我们定义基本类型的常量的时候

不也是吗

必须在定义的时候初始化

定义常对象的时候也必须初始化

而且一旦初始化以后

就不能再被更新了

在类中

我们还可以把类的成员

定义成const成员 常成员

这个常成员又包括常数据成员

和常函数成员

另外呢

我们很多时候会传递引用

去作为函数的参数 对吧

我们也知道引用是可以达到

数据双向传递的

那这个时候

如果我们既要用引用

又不想被引用的对象被更新

不想达到双向传递

那么怎么办呢

可以将参数定义为常引用

也就是只读的引用

另外在第六章

我们还会学习常数组

以及常指针

接下来我们首先来看看

如何定义一个常对象

以及常对象它有什么样的特点

那常对象呢

就是用const修饰的对象

比如说我们这样有一个a类

定义好了

接下来

我们要去定义a类的对象的时候

我们在普通的对象

定义的这种语法形式里面

加一个const

就说这个对象是常对象了

常对象它的特点是什么

就是不能再被更新了呀

不能被改变了呀

那我们再想一个问题

编译器做语法检查的时候

怎么能够保证你这个常对象

是不被改变的呢

好 我们把这个问题留着

后面会给大家介绍

专门用来处理常对象的函数

叫常函数

那么对象的成员

实际上也可以是const成员

在定义这些成员的时候

都可以加const修饰

把它定义成常成员

那常成员又分成常数据成员

和常成员函数

常成员函数就是我们刚才说了

专门用来处理常对象的

它是用const来修饰的

这样的函数

相当于它做出了承诺

它承诺说我绝不改变对象的状态

所以在这样的函数原型的最后

要加const

那么const也是重载函数的一个

区分的因素

那么常数据成员

就是用const修饰的数据成员

下面我们来看一个例题

体会一下常成员函数

它是有什么特点的

是用来干什么用的

在这个例题中呢

我们使用了常成员函数

大家看这个R类里面呢

定义了两个print函数

其中一个呢后面有const

是个常函数

const也是区分

不同重载形式的因素之一

在此之前我们只学过

要区分不同的重载函数

就要用不同的参数表

那么看这两个print函数

它的参数表都是一样的

都是空的

但是其中一个后缀了const

也是合法的重载形式

所以const也是区分

重载函数的一个因素

现在我们来看这个

print函数的实现

我们看到这两个print函数

它的函数体实现

基本上是一样的

但是

这个有const后缀的这个函数

它就特别地承诺了

这个函数它绝不改变对象的状态

那有同学可能觉得

我这第一个print函数

也并没有改变对象的状态

它只是输出R1 R2

对于状态没有什么改变

那么这个呢

必须要显式地标出来是const

编译器在编译的时候

才会去认真审查你这个函数体

看还有没有改变状态的语句

如果在其中有改变

对象状态的语句

它就会报语法错

但是如果你不后缀const

编译器在编译的时候

就不会去审查这一点

所以就不能保证这个函数

它不改变对象状态

我们有了这样的常函数

目的是什么呢

目的是为了处理常对象

我们知道一个对象

如果它定义的时候

定义为const

就说明这个对象

它是一个常量

一旦在定义的时候被初始化了

它就不能再被修改了

而修改对象的状态

有多少种办法呢 很多啊

你调用它每一个函数

都有可能修改对象的状态

都有这个危险性

不像基本类型的变量

你要修改它的状态

一个是给它赋值

一个是从键盘输入数据装进去

大概也就是这样的途径吧

但是对象呢

它有那么多的成员函数

我们这里头举例举的比较简单

其实可以罗列很多成员函数的

它对每个成员函数的运行

都有可能改变它的状态

那么编译器怎么控制这一点呢

就用常函数

也就是说

像这样定义的一个常对象

你只能用常函数去处理它

不是常函数

不能通过常对象调用

我们看这儿通过b

调用同一个函数b.print

那么调用的是谁呢

调用的就是

有const修饰的这个print

而a.print呢

调用的就是

没有const修饰的这个print

那么现在呢

我们考虑一个问题

如果这个print函数

反正它是不需要改变对象状态的

那么我也就没有必要

写两个版本了 对吧

我只要有一个常函数print就行了

不带const的这个print

我不要了行不行呢

希望大家课后去试一试

应该是可以的

如果你这个类里面

只有一个常函数

没有这个第一个print函数

那么a.print调用谁呢

它也去调用常函数

一个普通对象

调用常函数是没有任何问题的

所以通过这个例题呢

实际上我们得到了一种经验

在一个类中

如果你定义一个函数的时候

发现这个函数

就类似于print这样的函数

它的本意就是不打算改变

对象的状态

反正它是不会改变的

那么你就加一个const

加一个const

表示一种保证 一种承诺

承诺我不改变对象的状态

我可以用来处理常对象

所以如果能将一个类里面

不打算改变对象状态的函数

主动都说明为常函数

那么以后定义这个类

常对象的时候

使用起来就会比较方便

定义类数据成员的时候呢

我们也可以用const来修饰

那么接下来呢

我们再来看一个

类里面有const数据成员

也就是常数据成员的例子

我们看在这个例子中呢

我们定义了两个常数据成员

看看这个数据成员变量a

前面有const

这个a是属于每个对象的成员

再看这个数据成员b

它是一个静态成员

是属于整个类的

也定义为常成员

所以对象的成员 类的成员

都可以定义为常成员

一旦一个成员定义为常成员了

那么就只能在初始化的时候

给它初始值

不可能

也不允许在别的地方

再给它赋值了

我们看怎么来做初始化的
我们来看怎么做初始化的

首先看这个静态成员b

它在类体中声明

在类体外定义和初始化

所以它就需要在这儿进行初始化

初始化完了以后

就再也不许改变了

因为它是常量

再看这个对象的成员a

它是在构造每一个对象的时候

完成初始化的

比如我们来看这儿

构造对象a1的时候

给一个100作为初始值

然后这个时候会调用构造函数

我们看

在这个构造函数的初始化列表中

就用参数i去初始化了这个

常数据成员a

那么如果是其他的成员

如果不是常成员的话

我们知道

既可以在初始化列表中

完成初始化

也可以在函数体中进行赋值

至于有的成员呢

它不是一个简单的赋值

就可以初始化的

还要经过一些计算

才能得到初始值

那么在函数提中

可能它要发生一些计算

最后将计算结果

复给这个成员变量

但是这个常成员

它是不可以放在

构造函数的函数体中赋值的

它必须在初始化列表中

进行初始化

以后就不许再赋值了

像a2也是

a2用0作为参数去构造它

那么0传过来

就传到i里面

也是在初始化列表里面

它初始化的成员变量a里面了

接下来我们输出对象a1 a2的

它的数据成员a b

我们看到a1的数据成员a是100

b1的成员a是b.a 是0

它们的b成员都是10

因为这个b是一个静态成员

为所有的对象所共享的

那么什么是常引用呢

常引用也是用const修饰的引用

这样的引用相当于是个只读的

只能用它

去读取它指向对象的内容

但是不能去修改它指向的对象

下面我们来看一个

用常引用做参数的例子

用引用做参数

一方面

它可以实现双向的数据传递

另一方面

它传递参数的效率比较高

那么如果我们仅仅需要

这个高效率的特点

而不希望进行双向传递

这个时候我们就可以这个引用

加一个限制

让它是常引用

它也就是只读的引用了

下面我们在这个例题中

给大家演示

大家还记得在前面

友元函数的那个例子中

遗留的那个问题吗

当时我们用引用做函数的参数

这样能够提高参数传递的效率

但是也存在着一个隐患

就是说引用

它是可以双向传递数据的

用引用做参数呢

如果在函数中

对参数做了任何修改

就会影响到实参

现在我们就来解决这个问题

大家看仍然是以这个点类为例

仍然是在点类中

声明了一个友元函数dist

友元函数的函数体

跟我们上一个例子是一样的

计算两个点的距离

只不过呢

我们来看参数表

有了少许变化

在这两个参数前面都加了const

说明这两个参数都是常引用

常引用是什么意思呢

就是只读的引用

也就是说通过这个引用

只能读取

它所指向的对象里面的值

不能通过这个引用

去修改它指向的对象

具体到这个例子中呢

就是说通过引用p1

可以去取得p1.x的值 p1.y的值

但是你不能修改p1.x

不能修改p1.y

那p2也是一样

可以读取p2.x的值拿来用

读取p2.y的值拿来用

拿来参与运算

但是你都不能去修改它

那大家回去可以改一改这个题目

你看看编译器会不会给你报错

你就试图在这个dist函数里面

给p1.x重新赋值

或者给p2.y重新赋值

看看编译器会不会给你报错

肯定是会报错的

大家可以验证一下

这样你的印象就比较深刻了

好 那计算还是一样的

这样我们就可以比较放心地

用这个友元函数了

既提高了访问效率

让它能够直接访问私有成员

又不会因此对私有成员的安全性

有任何破坏

C++语言程序设计基础课程列表:

第1章 绪论

-导学

--第1章导学

-计算机系统简介

--计算机系统简介

--计算机系统简介 测试题

-计算机语言和程序设计方法的发展

--计算机语言和程序设计方法的发展

--计算机语言和程序设计方法的发展 测试题

-面向对象的基本概念

--面向对象的基本概念

--面向对象的基本概念 测试题

-程序的开发过程

--程序的开发过程

--程序的开发过程 测试题

-信息的表示和储存

--计算机中的信息与存储单位

--计算机的数字系统

--数据的编码表示

--信息的表示和储存 测试题

-实验指导

--实验一:VS开发环境介绍

第2章 C++简单程序设计(一)

-导学

--第二章导学

-C++语言概述

--C++的特点和程序实例

--C++字符集和词法记号

--C++语言概述 测试题

-基本数据类型、常量、变量

--基本数据类型、常量、变量

--程序举例

--基本数据类型、常量、变量 测试题

-运算与表达式

--算术运算与赋值运算

--逗号运算、关系运算、逻辑运算和条件运算

--Sizeof运算、位运算

--运算优先级、类型转换

--运算与表达式 测试题

-实验二:简单程序设计(上)

--实验二:简单程序设计(上)

第2章 C++简单程序设计(二)

-数据的输入和输出

--数据的输入和输出

--数据的输入和输出 测试题

-选择结构

--if语句

--switch语句

--选择结构 测试题

-循环结构

--循环结构——while语句

--do-while语句

--for语句

--嵌套的控制结构、其他控制语句

--循环结构 测试题

-自定义类型

--自定义类型

--自定义类型

-第2章小结

--第二章小结

-实验二:C++简单程序设计(下)

--实验二C++简单程序设计(下)

第3章 函数

-导学

--导学

-函数定义

--函数定义

--函数定义 测试题

-函数调用

--函数调用(例3-1)

--例3-2

--例3-3

--例3-4

--例3-5

--例3-6

--函数调用 测试题

-嵌套与递归

--函数的嵌套调用(例3-7)

--函数的递归调用(例3-8)

--例3-9

--例3-10

--嵌套与递归 测试题

-函数的参数传递

--函数的参数传递

--函数的参数传递 测试题

-引用类型

--引用类型(例3-11)

--引用类型 测试题

-含有可变参数的函数

--含有可变参数的函数

--含有可变参数的函数 测试题

-内联函数

--内联函数(例3-14)

--内联函数 测试题

-constexpr函数

--constexpr函数

--CONSTEXPR函数课后习题

-带默认参数值的函数

--带默认参数值的函数

--默认参数值例(3-15)

--带默认参数值的函数 测试题

-函数重载

--函数重载(例3-16)

--函数重载 测试题

-C++系统函数

--C++系统函数(例3-17)

--C++系统函数习题

-第3章小结

--第三章小结

-实验三(上)函数的应用

--实验三(上)函数的应用

-实验三(下)函数的应用

--实验三(下)函数的应用

第4章 类与对象

-导学

--导学

-面向对象程序的基本特点

--面向对象程序的基本特点

--面向对象程序的基本特点 测试题

-类和对象

--类和对象的定义

--类和对象的程序举例

--类和对象 测试题

-构造函数

--构造函数基本概念

--构造函数例题(1)——例4-1

--构造函数例题(2)——例4-2

--委托构造函数

--复制构造函数

--复制构造函数调用举例

--构造函数 测试题

-析构函数

--析构函数

--析构函数 测试题

-类的组合

--类的组合

--类的组合程序举例

--前向引用声明

--类的组合 测试题

-UML简介

--UML简介

--UML简介课后习题

-结构体与联合体

--结构体(例4-7)

--联合体(例4-8)

--结构体与联合体 测试题

-枚举类

--枚举类

--枚举类 测试题

-第4章小结

--第四章小结

-实验四(上)

--实验四(上)

-实验四(下)

--实验四(下)

第5章 数据的共享与保护

-导学

--导学

-标识符的作用域与可见性

--标识符的作用域与可见性

--标识符的作用域与可见性 测试题

-对象的生存期

--对象的生存期

--对象的生存期 测试题

-类的静态成员

--静态数据成员(例5-4)

--静态函数成员(例5-5)

--类的静态成员 测试题

-类的友元

--类的友元(例5-6)

--类的友元 测试题

-共享数据的保护

--共享数据的保护(例5-7)

--共享数据的保护 测试题

-多文件结构和预编译命令

--多文件结构和预编译命令(例5-10)

--多文件结构和预编译命令 测试题

-第5章小结

--小结

-实验五

--实验五

第6章 数组、指针与字符串(一)

-导学

--导学

-数组的定义与初始化

--数组的定义与使用

--数组的储存与初始化

--一维数组应用举例

--数组的定义与初始化 测试题

-数组作为函数的参数

--数组作为函数参数(例6-2)

--数组作为函数的参数 测试题

-对象数组

--对象数组

--对象数组 测试题

-基于范围的for循环

--基于范围的for循环

-指针的定义和运算

--指针的概念、定义和指针运算

--指针的初始化和赋值

--指针的算术运算、关系运算

--指针的定义和运算 测试题

-综合实例

--综合实例

-实验六(上)

--实验六上

第6章 数组、指针与字符串(二)

-指针与数组

--用指针访问数组元素

--指针数组

--指针与数组 测试题

-指针与函数

--以指针作为函数参数

--指针类型的函数

--指向函数的指针

--指针与函数 测试题

-对象指针

--对象指针

--对象指针 测试题

-动态内存分配

--动态分配与释放内存

--申请和释放动态数组(一)

--申请和释放动态数组(二)

--动态内存分配 测试题

-智能指针

--智能指针

-vector对象

--vector对象

--vector对象 测试题

-对象复制与移动

--深层复制与浅层复制

--移动构造

--对象复制与移动 测试题

-字符串

--C风格字符串

--string类

--字符串 测试题

-第6章小结

--第六章小结

-综合实例

--综合实例

-实验六(下)

--实验六(下)

共享数据的保护(例5-7)笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。