第二章 继承和派生

2.1 派生

派生类或派生类的使用者均不能访问基类的私有数据成员;对于基类的公有成员的访问,如果派生的方式不同,访问的权限也不同。派生时,不指明派生类型的,按私有派生进行派生。

<1>私有派生

由私有派生得到的派生类,对基类的公有成员只能是私有继承,也就是基类的所有公有成员只能成为派生类的私有成员,这些私有成员只能被派生类的成员函数访问,派生类的使用者无权访问。此时,基类中的保护段成员也成为派生类的私有成员。

如果希望基类中的某些公有成员、保护成员在私有派生类中也是公有、保护的,使得派生类和派生类的使用者可以访问它,则可以在派生类中的公有段或保护段中说明这些成员,并在成员名前缀上“类名::”。

class A {

int count;

public:

A(int i = 0){

count = i; }

int getCount(){

return count; }

};

class B :A{ //私有派生

public:

A::getCount; //声明基类的公有成员为派生类的公有成员

/*protected:

A::getCount; */ //声明基类的公有成员为派生类的保护成员

};

void main()

{

B b;

int num = b.getCount();

}

声明基类的公有成员为派生类的公有成员,只需要注明类名和函数名,不需要声明函数参数和返回值,因为该函数在基类中已经声明过。

私有段中不能进行声明。派生类中有与基类相同的函数时,这种声明是无效的,派生对象调用的函数是派生类中的,而不是基类的。在派生类公有段中声明基类的构造函数,如果基类构造函数(重载情况下)同在公有段中,则基类的构造函数成为派生类中的公有成员;如果基类构造函数处于不同的段中,系统将根据派生类构造函数调用基类构造函数的使用,决定这种声明的有效性。

Class A {

protected:

int count;

void print1();

public:

int num;

void print();

A();

A(int i);

};

class B: A{

protected:

A::count;

A::print1;

public:

B();

void print();

//A::count; //error不能同时声明

A::A;

A::num;

A::print; //声明无效

};

class A {

protected:

A(int i);

public:

A();

};

class B: A{

public:

B():A(){

}

A::A;  //public段A()有效

/* B():A(2){

}

A::A;  */ //protected段A()有效

};

<2>公有派生

通过公有派生得到派生类,基类中的公有成员在派生类中仍然是公有成员,基类中的保护段成员在派生类中仍然是保护成员。

<3>保护派生

通过公有派生得到派生类,基类中的公有成员和保护段成员,在派生类中是保护成员。

2.2 派生类的构造函数和析构函数

2.2.1派生类构造函数和析构函数的定义原则

在以下情况下必须定义派生类的构造函数

<1>派生类本身需要构造函数

<2>在定义派生类对象时,其相应的基类需要调用带参数的构造函数。

构造函数格式:

派生类构造函数(参数):基类构造函数(参数),对象成员1构造函数(参数),。。。对象成员n构造函数(参数) { //基类和对象成员构造函数的排放顺序不分先后

}

如果基类使用缺省的构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数(参数)”;如果派生类对象不需要初始化,也不必定义构造函数。

派生类的析构函数的定义与基类没有关系。

如果基类、成员类和派生类都有构造函数,执行顺序分别是先基类、再对象成员、后派生;析构函数时的执行顺序正好相反,先派生、再对象成员、后基类。

class A {

int count;

public:

A(int i = 0){

count = i;

}

~A(){

}

};

class B{

public:

B(char *str){

memset(s, 0, 20);

strcpy(s, str);

}

~B(){

}

private:

char s[20];

};

class C :A{

public:

C(int num);

~C(){

}

private:

int number;

B b;

};

C::C(int num):b("CHINA"),A(num){

number = num;

}

void main(void )

{

C c(100);  //先A(num),再b("CHINA"),最后C::C(int num)

} //析构时,先~C(),再~B(),最后~A()

2.2.2虚析构函数:

c++语言标准关于虚析构函数的阐述:当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。实际运行时经常发生的是,派生类的析构函数永远不会被调用。声明析构函数为虚就会调用基类和派生类的析构函数。

#include <iostream.h>

class Base

{

public:

virtual ~Base() { cout<< "~Base" << endl ; }

};

class Derived : public Base

{

public:

virtual ~Derived() { cout<< "~Derived" << endl ; }

};

void main(void)

{

Base * pB = new Derived;  // upcast

delete pB; //对象内存释放时,Derived和Base的析构函数都会被调用。

}

输出结果为:

~Derived

~Base

如果析构函数不为虚,那么输出结果为

~Base

2.3 派生类对基类私有成员的访问

在公有派生和私有派生中,继承类都不能直接访问基类的私有成员。继承类访问基类的私有成员的方法有以下两种方法:

<1>在基类中增加保护类成员

将基类私有成员中需要提供给派生类访问的部分定义为保护段成员。基类的保护类成员可以被派生类(无论公有派生、保护派生还是私有派生)访问,但外界对象不能访问。

class A {

protected:

int count;

public:

A(int i = 0){

count = i;

}

void print(){

cout << count;

}

};

class B:public A{

int num;

public:

B(int i){ num = i;}

void print(){

cout << count << " " << num; //公有继承派生类访问基类的保护成员

}

};

class C :B{

public:

C(): B(10){ }

void print(){

cout << count; //私有继承的派生类访问基类的保护成员

//cout << num; //私有继承的派生类不能访问基类的私有成员

}

};

void main()

{

B b(4);

C c;

b.print();

c.print();

}

<2>将需要访问基类私有成员的派生类成员函数声明为基类的友元

在基类中增加保护类成员的方法,存在一个弊端,多重公有继承或保护继承得到的派生类,仍然可以访问原基类的保护段成员。可以通过将需要访问基类私有成员的派生类成员函数声明为基类的友元的方法,使得派生类中的指定成员函数函数可以访问基类的私有成员,其他的成员函数则不能访问基类的私有成员。

通过友员访问基类的私有成员,这种访问不是经常的,但是必要的。否则,需要类提供足够的成员函数。友元的使用使得C++中的数据封装性受到削弱,导致程序的维护性变差(慎用)。

声明为某个类的友元的外界对象既可以是另一个类的成员函数,也可以是不属于任何类的一个函数,还可以是某个类(此时,该类中的所有成员函数都成为友元)。设置友元的首要目的是可以使友元函数访问(使用或改变)某类的私有成员。

友元的声明可以放在类的公有部分,也可以放在类的私有部分。

class circle;

class point {

int x, y;

public:

point(int x, int y);

friend void print(const point p); //声明不属于任何类的函数为友元函数

friend class circle; //声明某类.为友元类

friend void circle::getCircleCenter(int &x, int &y); //声明某类的函数为友元函数,该函数可以访问point的私有成员x和y

}

说明:友元函数虽然可以访问类对象的私有成员,但是它没有this指针,在某一时刻究竟是应该访问哪一个对象的私有成员,很难确定。可以通过向友元函数传递对象的办法,显式地确定对象。

友元关系是“给予”的,而不是“索取”的。也就是说,如果类B是类A的友元类,类A中必须声明类B是类A的友元类。友元关系是不对称且不传递的。

2.4 虚基类

P part(of A)

P part(of B)

C part

C

A

B

P

P

C

A

B

P

在如下左图所示的多层继承类中,类P两次成为类C的间接基类,此时,C类对象中有两个类P的对象:由A继承的P和由B继承的P,C类对象的内存排列如中图所示:

虚基类:当在多条继承路径上有一个公共的基类,在这些路径中的某几条路径汇合处,这个公共基类会产生多个实例,如果需要只保存基类的一个实例,可以将这个公共基类声明为虚基类。虚基类的声明如下表右侧所示:

非虚基类的多重继承

class P

{

public:

int next;

};

class A: public P

{

};

class B: public P

{

};

class C: public A, public B

{

public:

void set( int i);

};

void C::set(int i)

{

next = i;  //具有二义性

}

虚基类的多重继承

class P

{

public:

int next;

};

class A: virtual public P

{

};

class B: virtual public P

{

};

class C: public A, public B

{

public:

void set( int i);

};

void C::set(int i)

{

next = i;  //无二义性

}

虚基类构造函数的调用规则:

<1>同一层次中虚基类的构造函数在非虚基类之前调用

<2>同一层次中包含多个虚基类,虚基类构造函数按照他们的说明顺序调用

<3>若虚基类由非虚基类派生,则遵守先调用基类构造函数,再调用派生类构造函数。

例:

base2类

base2类

base虚基类

level1类

level2虚基类

toplevel类

如下所示的类继承关系:

建立toplevel类对象top时,构造函数的调用顺序是:

base类

base2类

level2类

base2类

level1类

toplevel类

转载于:https://www.cnblogs.com/java201408/archive/2006/10/27/3901062.html

C/C++学习----第二章 继承和派生相关推荐

  1. Python爬虫学习第二章-1-requests模块简介

    Python爬虫学习第二章-1-requests模块简介   这一章主要是介绍requests模块的相关知识以及使用 1.requests模块简介: 概述:是python中原生的一款基于网络请求的模块 ...

  2. 深度学习 - 第二章 - 机器学习基础

    深度学习 - 第二章 - 机器学习基础 第二章 机器学习基础 2.1 各种常见算法图示 2.2 监督学习.非监督学习.半监督学习.弱监督学习? 2.3 监督学习有哪些步骤 2.4 多实例学习? 2.5 ...

  3. Ruby学习-第二章

    第二章 类继承,属性,类变量 1.如何声明一个子类 class Treasure < Thing 这样Thing类中的属性name,description都被Treasure继承 2.以下三种方 ...

  4. Java OOP 第二章 继承

    Java OOP 继承 文章目录 Java OOP 继承 一.学习目标 二.继承 三.类的继承关系示例 四.深入理解继承 五.Object类 六.方法重写 七.本章总结 一.学习目标 掌握继承的优点和 ...

  5. 计算机视觉与深度学习第二章:图像分类任务

    计算机视觉与深度学习 本文按照北京邮电大学计算机学院鲁鹏老师的计算机视觉与深度学习课程按章节进行整理,需要的同学可借此系统学习该课程详尽知识~ 第二章 图像分类任务 计算机视觉与深度学习 一.什么是图 ...

  6. ABAP学习-第二章[HELLO WORLD]

                            第二章 创建"HELLO WORLD"程序本章将介绍建立"HELLO WORLD"程序,为新建程序分配TCODE ...

  7. 图解Http学习第二章

    Http通信必须存在客户端和服务端 请求从客户端发出,服务器端接收后响应请求.(所以不难理解:首先是从客户端开始建立通信的) 发送请求报文示例: GET /index.htm  HTTP/1.1 Ho ...

  8. 零基础入门深度学习 | 第二章:线性单元和梯度下降

    北京 | 高性能计算之GPU CUDA课程11月24-26日3天密集学习 快速带你晋级阅读全文> 无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作 ...

  9. Intel汇编语言程序设计学习-第二章 IA-32处理器体系结构-上

    第2章  IA-32处理器体系结构 2.1  基本概念 本章以程序员的视角描述Intel IA-32处理器家族机器计算机系统,包括所有的Intel兼容处理器,如AMD的速龙(Athlon)和皓龙(Op ...

最新文章

  1. 【Sublime Text3】Package Control:Install Package不能使用解决方法
  2. pointnet与pointnet++
  3. hihoCoder1233(2015北京网络赛H题)
  4. rimraf --A deep deletion module for node (like `rm -rf`)
  5. [css] 使用rem布局时怎样合理设置根标签字体大小?
  6. Python字典(二)
  7. [Flink] Flink的应用场景
  8. Jfinal Quartz 插件
  9. 请简述GC(垃圾回收)产生的原因,并描述如何避免?
  10. LabVIEW程序测试
  11. 第一周学习报告(关于string)
  12. Web大学生网页作业成品——个人班级网站设计与实现(HTML+CSS)
  13. 基于知识图谱和图卷积神经网络的应用——学习笔记
  14. 【零样本知识蒸馏】(六)NeutIPS 2019:Zero-shot knowledge transfer via adversarial belief matching
  15. 深度理解PHP执行流程
  16. Cesium专栏-空间分析之坡向分析(附源码下载)
  17. iPhone无法充电小技巧
  18. 前端攻城狮的自我修养1
  19. 第二十三章 天猫精灵控制ESP32(wifi+tcp+json)
  20. 神经网络原理与实例精解,神经网络计算机的组成

热门文章

  1. vue取通过key取value_彻底理解Vue中的Watcher、Observer、Dep
  2. win7 mysql添加到服务_MySQL服务添加到Windows系统服务中
  3. 函数运行 形参实参变化 内存空间_可能python创始人都不知道的,python函数实参形参讲解...
  4. 北海计算机职称考试地点,【2017年广西北海职称计算机考试报名时间9月1日-5日】- 环球网校...
  5. Eclipse和IDEA 简单对比说明
  6. ES学习笔记之-ClusterState的学习
  7. Hadoop配置机架感知
  8. 2020国货品牌力发展报告
  9. 中国在线度假旅游市场专题分析2020
  10. python vars name报错_Python vars()全局名称错误