C++ 面向对象(三)—— 类之间的关系

友元函数(Friend functions)

在前面的章节中我们已经看到了对class的不同成员存在3个层次的内部保护:public, protected 和 private。在成员为 protected 和 private的情况下,它们不能够被从所在的class以外的部分引用。然而,这个规则可以通过在一个class中使用关键字friend来绕过,这样我们可以允许一个外部函数获得访问class的protected 和 private 成员的能力。

为了实现允许一个外部函数访问class的private 和 protected 成员,我们必须在class内部用关键字friend来声明该外部函数的原型,以指定允许该函数共享class的成员。在下面的例子中我们声明了一个 friend 函数 duplicate:

    // friend functions#include <iostream.h>class CRectangle {int width, height;public:void set_values (int, int);int area (void) {return (width * height);}friend CRectangle duplicate (CRectangle);};void CRectangle::set_values (int a, int b) {width = a;height = b;}CRectangle duplicate (CRectangle rectparam) {CRectangle rectres;rectres.width = rectparam.width*2;rectres.height = rectparam.height*2;return (rectres);}int main () {CRectangle rect, rectb;rect.set_values (2,3);rectb = duplicate (rect);cout << rectb.area();}
24

函数duplicate是CRectangle的friend,因此在该函数之内,我们可以访问CRectangle 类型的各个object的成员 width 和 height。注意,在 duplicate()的声明中,及其在后面main()里被调用的时候,我们并没有把duplicate 当作class CRectangle的成员,它不是。

friend 函数可以被用来实现两个不同class之间的操作。广义来说,使用friend 函数是面向对象编程之外的方法,因此,如果可能,应尽量使用class的成员函数来完成这些操作。比如在以上的例子中,将函数duplicate() 集成在class CRectangle 可以使程序更短。


友元类 (Friend classes)

就像我们可以定义一个friend 函数,我们也可以定义一个class是另一个的friend,以便允许第二个class访问第一个class的 protected 和 private 成员。

    // friend class#include <iostream.h>class CSquare;class CRectangle {int width, height;public:int area (void) {return (width * height);}void convert (CSquare a);};Class CSquare {private:int side;public:void set_side (int a){side=a;}friend class CRectangle;};void CRectangle::convert (CSquare a) {width = a.side;height = a.side;}int main () {CSquare sqr;CRectangle rect;sqr.set_side(4);rect.convert(sqr);cout << rect.area();return 0;}
16

在这个例子中,我们声明了CRectangle 是CSquare 的friend,因此CRectangle可以访问CSquare 的protected 和 private 成员,更具体地说,可以访问CSquare::side,它定义了正方形的边长。

在上面程序的第一个语句里你可能也看到了一些新的东西,就是class CSquare空原型。这是必需的,因为在CRectangle 的声明中我们引用了CSquare (作为convert()的参数)。CSquare 的定义在CRectangle的后面,因此如果我们没有在这个class之前包含一个CSquare 的声明,它在CRectangle中就是不可见的。

这里要考虑到,如果没有特别指明,友元关系(friendships)并不是相互的。在我们的CSquare 例子中,CRectangle 是一个friend类,但因为CRectangle 并没有对CSquare作相应的声明,因此CRectangle 可以访问CSquare 的 protected 和private 成员,但反过来并不行,除非我们将 CSquare 也定义为CRectangle的 friend。


类之间的继承(Inheritance between classes)

类的一个重要特征是继承,这使得我们可以基于一个类生成另一个类的对象,以便使后者拥有前者的某些成员,再加上它自己的一些成员。例如,假设我们要声明一系列类型的多边形,比如长方形CRectangle或三角形CTriangle。它们有一些共同的特征,比如都可以只用两条边来描述:高(height)和底(base)。

这个特点可以用一个类CPolygon 来表示,基于这个类我们可以引申出上面提到的两个类CRectangle 和 CTriangle 。

类CPolygon 包含所有多边形共有的成员。在我们的例子里就是: width 和 height。而CRectangle 和 CTriangle 将为它的子类(derived classes)。

由其它类引申而来的子类继承基类的所有可视成员,意思是说,如果一个基类包含成员A ,而我们将它引申为另一个包含成员B的类,则这个子类将同时包含 A 和 B。

要定义一个类的子类,我们必须在子类的声明中使用冒号(colon)操作符: ,如下所示:

class derived_class_name: public base_class_name;

这里derived_class_name 为子类(derived class)名称,base_class_name 为基类(base class)名称。public 也可以根据需要换为protected 或 private,描述了被继承的成员的访问权限,我们在以下例子后会很快看到:

    // derived classes#include <iostream.h>Class CPolygon {protected:int width, height;public:void set_values (int a, int b) { width=a; height=b;}};class CRectangle: public CPolygon {public:int area (void){ return (width * height); }};class CTriangle: public CPolygon {public:int area (void){ return (width * height / 2); }};int main () {CRectangle rect;CTriangle trgl;rect.set_values (4,5);trgl.set_values (4,5);cout << rect.area() << endl;cout << trgl.area() << endl;return 0;}
20
10

如上所示,类 CRectangle 和 CTriangle 的每一个对象都包含CPolygon的成员,即: width, height 和 set_values()。

标识符protected 与 private类似,它们的唯一区别在继承时才表现出来。当定义一个子类的时候,基类的protected 成员可以被子类的其它成员所使用,然而private 成员就不可以。因为我们希望CPolygon的成员width 和 height能够被子类CRectangle 和 CTriangle 的成员所访问,而不只是被CPolygon自身的成员操作,我们使用了protected 访问权限,而不是 private。

下表按照谁能访问总结了不同访问权限类型:

可以访问publicprotectedprivate
本class的成员yesyesyes
子类的成员yesyesno
非成员yesnono

这里"非成员"指从class以外的任何地方引用,例如从main()中,从其它的class中或从全域(global)或本地(local)的任何函数中。

在我们的例子中,CRectangle 和CTriangle 继承的成员与基类CPolygon拥有同样的访问限制:

   CPolygon::width           // protected accessCRectangle::width         // protected accessCPolygon::set_values()    // public accessCRectangle::set_values()  // public access

这是因为我们在继承的时候使用的是public,记得我们用的是:

class CRectangle: public CPolygon;

这里关键字 public 表示新的类(CRectangle)从基类(CPolygon)所继承的成员必须获得最低程度保护。这种被继承成员的访问限制的最低程度可以通过使用 protected 或 private而不是public来改变。例如,daughter 是mother 的一个子类,我们可以这样定义:

class daughter: protected mother;

这将使得protected 成为daughter 从mother处继承的成员的最低访问限制。也就是说,原来mother 中的所有public 成员到daughter 中将会成为protected 成员,这是它们能够被继承的最低访问限制。当然这并不是限制daughter 不能有它自己的public 成员。最低访问权限限制只是建立在从mother中 继承的成员上的。

最常用的继承限制除了public 外就是private ,它被用来将基类完全封装起来,因为在这种情况下,除了子类自身外,其它任何程序都不能访问那些从基类继承而来的成员。不过大多数情况下继承都是使用public的。

如果没有明确写出访问限制,所有由关键字class 生成的类被默认为private ,而所有由关键字struct 生成的类被默认为public。


什么是从基类中继承的? (What is inherited from the base class?)

理论上说,子类(drived class)继承了基类(base class)的所有成员,除了:

  • 构造函数Constructor 和析构函数destructor
  • operator=() 成员
  • friends

虽然基类的构造函数和析构函数没有被继承,但是当一个子类的object被生成或销毁的时候,其基类的默认构造函数 (即,没有任何参数的构造函数)和析构函数总是被自动调用的。

如果基类没有默认构造函数,或你希望当子类生成新的object时,基类的某个重载的构造函数被调用,你需要在子类的每一个构造函数的定义中指定它:

derived_class_name (parameters) : base_class_name (parameters) {}

例如 (注意程序中黑体的部分):

    // constructors and derivated classes#include <iostream.h>class mother {public:mother (){ cout << "mother: no parameters\n"; }mother (int a){ cout << "mother: int parameter\n"; }};class daughter : public mother {public:daughter (int a){ cout << "daughter: int parameter\n\n"; }};class son : public mother {public:son (int a) : mother (a){ cout << "son: int parameter\n\n"; }};int main () {daughter cynthia (1);son daniel(1);return 0;}
mother: no parameters
daughter: int parameter

mother: int parameter
son: int parameter

观察当一个新的daughter object生成的时候mother的哪一个构造函数被调用了,而当新的son object生成的时候,又是哪一个被调用了。不同的构造函数被调用是因为daughter 和 son的构造函数的定义不同:

   daughter (int a)          // 没有特别制定:调用默认constructorson (int a) : mother (a)  // 指定了constructor: 调用被指定的构造函数

多重继承(Multiple inheritance)

在C++ 中,一个class可以从多个class中继承属性或函数,只需要在子类的声明中用逗号将不同基类分开就可以了。例如,如果我们有一个特殊的class COutput 可以实现向屏幕打印的功能,我们同时希望我们的类CRectangle 和 CTriangle 在CPolygon 之外还继承一些其它的成员,我们可以这样写:

class CRectangle: public CPolygon, public COutput {
class CTriangle: public CPolygon, public COutput {

以下是一个完整的例子:

    // multiple inheritance#include <iostream.h>class CPolygon {protected:int width, height;public:void set_values (int a, int b){ width=a; height=b;}};class COutput {public:void output (int i);};void COutput::output (int i) {cout << i << endl;}class CRectangle: public CPolygon, public COutput {public:int area (void){ return (width * height); }};class CTriangle: public CPolygon, public COutput {public:int area (void){ return (width * height / 2); }};int main () {CRectangle rect;CTriangle trgl;rect.set_values (4,5);trgl.set_values (4,5);rect.output (rect.area());trgl.output (trgl.area());return 0;}
20
10

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

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

相关文章

五年后存储会是什么样子

原文&#xff1a;http://chucksblog.emc.com/chucks_blog/2013/06/what-storage-might-look-like-in-five-years.html注明&#xff1a;本文内容基于 VMware VSAN beta 版本撰写&#xff0c;请访问http://www.vmware.com/products/virtual-san/获得有关正式版本的更新信息。有时…

java.lang.NoClassDefFoundError: javax/transaction/Synchronization (jUnit测试报错)

测试hibernate报错原因项目缺少包在 hibernate 解压目录下找到 jta.jar 文件往项目中添加该 jar 包&#xff0c;即可解决添加方法&#xff1a;【右击项目】-->【构建路径】....来自为知笔记(Wiz)转载于:https://www.cnblogs.com/zhanyao/p/3711322.html

yarn oom问题一例

线上部分job运行失败&#xff0c;报OOM的错误:因为是maptask报错&#xff0c;怀疑是map数量过少&#xff0c;导致oom&#xff0c;因此调整参数&#xff0c;增加map数量&#xff0c;但是问题依然存在。看来和map的数量没有关系。通过jobid查找jobhistory中对应的日志信息&#x…

C++ 高级篇(三)—— 出错处理

本节介绍的出错处理是ANSI-C 标准引入的新功能。如果你使用的C 编译器不兼容这个标准&#xff0c;则你可能无法使用这些功能。 在编程过程中&#xff0c;很多时候我们是无法确定一段代码是否总是能够正常工作的&#xff0c;或者因为程序访问了并不存在的资源&#xff0c;或者由…

C++ 高级篇(一)—— 模板(Templates)

模板(Templates)是ANSI-C 标准中新引入的概念。如果你使用的 C 编译器不符合这个标准&#xff0c;则你很可能不能使用模板。 函数模板( Function templates) 模板(Templates)使得我们可以生成通用的函数&#xff0c;这些函数能够接受任意数据类型的参数&#xff0c;可返回任意类…

PPT到底是天使还是魔鬼?

说老实话&#xff0c;我非常不喜欢那些PPT的培训者一味地鼓吹PPT的重要性&#xff0c;视觉化思维的重要性&#xff0c;PPT在演讲中的重要性等等&#xff0c;我很清楚他们这么说是因为他们必须这么说&#xff0c;因为如果不把PPT的重要性强调一下&#xff0c;谁来上他的课呢&…

ultraedit正则表达式

一般使用ultraedit中的Perl风格的正则表达式&#xff0c;下面是perl正则的基本语法 perl中的元字符如下&#xff1a; ^ 表示一行的开头&#xff1b; $ 表示一行的结尾&#xff1b; ( ) 表示一个匹配块的&#xff0c;可以对匹配上的块通过$1,$2...进行读取&#xff0c;…

微软云介绍

微软云介绍 http://msdn.microsoft.com/zh-cn/ff380142 什么是云开发&#xff1f;&#xff08;概述&#xff09; 云计算是指远程运行并通过 Internet 访问的计算机和应用程序。在云计算中&#xff0c;虚拟机在大型数据中心中运行&#xff0c;并取代了物理 PC 和服务器。通过将许…

IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口&#xff0c;通常包括&#xff1a;控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式&#xff0c;可以把CPU分成两大类。一类CPU&#xff08…

Win8 HTML5与JS编程学习笔记(二)

近期一直受到win8应用的Grid布局困扰&#xff0c;经过了半下午加半个晚上的奋斗&#xff0c;终于是弄明白了Grid布局方法的规则。之前我是阅读的微软官方的开发教程&#xff0c;书中没有详细说明CSS3的布局规则&#xff0c;自己鼓捣了半天也是一头雾水&#xff0c;于是又找到了…

Windows下的Qt Creator的安装

采用Qt和Qt creator分别下载和安装的方式&#xff1a;&#xff08;需要手动设置关联Qt和Qt Creator&#xff09; 一、软件下载 从http://qt-project.org/downloads分别下载Qt和Qt Creator&#xff1a; Qt使用4.7.2版本&#xff1a;qt-win-opensource-4.7.2-mingw.exe Qt Creato…

进程 、进程组、会话、控制终端之间的关系

一个进程组可以包含多个进程 进程组中的这些进程之间不是孤立的&#xff0c;他们彼此之间或者存在者父子、兄弟关系&#xff0c;或者在功能有相近的联系。 那linux为什么要有进程组呢&#xff1f;其实提供进程组就是方便管理这些进程。假设要完成一个任务&#xff0c;需要同时并…

Angularjs 通过asp.net web api认证登录

Angularjs 通过asp.net web api认证登录 Angularjs利用asp.net mvc提供的asp.net identity&#xff0c;membership实现居于数据库的用户名/密码的认证登录 环境 Vs.net 2013 Asp.net mvc web api Individual user accounts Angularjs Underscore 新建一个asp.net mvc web api …

PANIC: Unreachable code reached.

为什么80%的码农都做不了架构师&#xff1f;>>> Caused by: java.lang.RuntimeException: PANIC: Unreachable code reached.at cryptix.jce.provider.cipher.BlockCipher.engineGetParameters(BlockCipher.java:244)at javax.crypto.Cipher.checkCryptoPerm(Ciphe…

[LeetCode Online Judge]系列-求二维平面内在一条直线上的最大点数

2019独角兽企业重金招聘Python工程师标准>>> Max Points on a Line Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 题目是:在2D平面内给N个点,求最多多少个点在一个直线上. 以下是AC的解决方式: /*** Defi…

【思科】GNS3模拟静态NAT/动态NAT

实验拓扑&#xff1a;实验目的&#xff1a;利用NAT技术&#xff0c;使C1,C2能与R2连通。实验步骤&#xff1a;如图配置好IP地址&#xff0c;为C1,C2指定网关192.168.1.1。2.在R1上配置静态NAT.假定为C1配置公网地址12.0.0.3&#xff0c;来实现与R2的连通。这样&#xff0c;静态…

xml 解析

2019独角兽企业重金招聘Python工程师标准>>> 各大数据接口大全&#xff1a;http://blog.sina.com.cn/s/articlelist_2127818045_10_1.html package com.test.junit; import static org.junit.Assert.*; import java.io.ByteArrayInputStream;import java.io.InputSt…

理解Lucene/Solr的缓存

缓存对于提高搜索引擎的吞吐量&#xff0c;降低CPU占用率极为重要。Lucene/Solr在这块做了很多的工作。Lucene/Solr中默认提供了5种缓存&#xff0c;同时solr还提供扩展缓存接口&#xff0c;允许开发者自定义缓存。1 缓存的基本原理Solr实现了两种策略的缓存&#xff1a;LRU(Le…

深入了解tcmalloc(一):windows环境下无缝拦截技术初探

2019独角兽企业重金招聘Python工程师标准>>> 概述&#xff1a; 又到了一个总结提炼的阶段&#xff0c;这次想具体聊聊游戏引擎中使用的内存管理模块tcmalloc组件的使用心得。项目的前期曾经遇到过内存瓶颈&#xff0c;特别是windows系统下的客户端程序在经历长时间运…

删除数据库日志文件的方法

原文:删除数据库日志文件的方法你曾经有在执行SQL的时候&#xff0c;数据库报事务日志已满&#xff0c;然后执行报错。然后纠结于怎么删除数据库日志&#xff0c;捣鼓半天吗&#xff0c;现在就提供两种删除日志文件的方法&#xff0c;希望能够帮到你&#xff01; 阅读目录 方法…