包。继承,多态性C++的三个基本概念,在这里,我们重点总结继承的东西





1 类派生列表

类派生列表中指定一个派生类继承基类,来自列表与一个或多个基类如:

class B : public A1,protected A2,private A3

可是单继承时最常见的,多继承不多见





2 派生类的定义

派生类继承时,会包括父类的全部成员。即便私有成员不能被訪问。父类中的虚函数,在派生类中一般也要定义,如





果不定义的话。派生类将继承基类的虚函数





3 基类必须是已经定义的

一个只声明的类,是不能出如今派生类的类派生列表中。因此派生类非常可能要訪问基类的列表,可是假设基类还没





被定义,所以这样做是错误的。

4 派生类的声明

派生类的列表不能出如今派生类的列表中,如:

class B : public A; //error

正确的声明应是:

Class B;

Class A;





5 继承修饰符

C++中的继承方式有三种。public。protected,private继承(简称3p)。

无论是何种继承方式,基类中的private成员





都不能被派生的用户代码和成员定义代码訪问。接下来详细说说三种继承方式:

<1>private继承,基类的全部成员在派生类中都是private成员,可是基类的private成员依然不能被訪问

<2>protected继承,基类的public,protected成员变成子类的protected成员,基类的private成员依然不能被訪问

<3>public继承,基类的public变成派生类的public。protected变成派生类的protected。基类的private不能被訪问





6 protected 修饰符

当protected修饰类成员的时候,类成员能够被类自身成员定义所使用。可是不能被用户代码使用,这点类似private

其次,protected修饰的成员能够被子类訪问到。这点类似public,可是不用于private。

7 不能被继承的成员

构造函数,复制控制成员(复制构造函数,赋值操作符,析构函数)这是不能被继承的函数





8 派生类的构造函数

派生类构造函数一般格式:

DeriveClass(xxx):baseClass(xxx),x1(x),x2(x)....

<1> 派生类的构造函数除了要初始化自己新定义的成员外,还要初始化基类的成员。顺序是先调用基类的构造函数初始化基类的成员。然后再初始化自己的新成员(顺序是声明的顺序)

<2> 假设没有自定义构造函数。编译器会合成一个默认的构造函数,先调用基类的默认构造函数,然后就再去初始化其它新的成员。

<3> 这里须要注意的是:假设基类没有默认构造函数。那么编译器就不能合成默认构造函数,假设这个时候再不去定义派生类的默认构造函数,那样就会出错。诸如--“error C2512: “Derive”: 没有合适的默认构造函数可用。”

<4> 假设不在派生类构造函数的初始化列表中指定基类的构造函数,则会调用基类的默认构造函数

<5> 派生类构造函数的默认实參派生类能够将构造函数的全部參数都设置为默认參数,能够使用0-n个默认參数



9 派生类中的复制控制

复制控制包含复制构造函数,赋值操作符。析构函数

<1>未定义复制构造函数。编译器会自己合成一个复制构造函数,它会调用基类的复制构造函数

<2>自己定义复制构造函数,一般要调用基类的复制构造函数,否则会出意外

<3>未定义赋值操作符。编译会自己主动合成一个赋值操作符,它会调用基类的复制操作符

<4>自己定义赋值操作符要提防自身赋值。一般都是这么做

  1. Derive& Derived::operator =(Derived& rh)
  2. {
  3. if(this != &rh)
  4. {
  5. Base::operator=(rh);
  6. //do whatever needed to clean up the old value
  7. //assign the members from the derived
  8. }
  9. }

这样做的原因就是为了防止自身赋值。在自身赋值的时候。我们通常要先把动态开辟的内存clear掉。假设不加上if(this != &rh)的话。那么在进行自身赋值的时候,就会把自己给废了(clear掉自己)。

<5> 派生类析构函数

这个和前两个不同,派生类的析构函数仅仅负责自己新定义对象的析构,不负责基类对象的析构。在派生类对象析构的时候,编译器会先调用派生类自己的析构函数,然后调用基类的析构函数。

每一个类无论基类还是派生类,都仅仅负责清除自己的成员。

以上的复制控制演示样例代码例如以下所看到的:

  1. #pragma once
  2. #include <iostream>
  3. using namespace std;
  4. class Base
  5. {
  6. public:
  7. Base(){ m = 10;cout<<"Base default constructor"<<endl;}
  8. Base(int x):m(x){}
  9. Base(Base& rh){cout<<"the base copy constructor"<<endl;};
  10. ~Base(void){cout<<"Base destructor"<<endl;}
  11. Base& operator = (Base& base){ this->m = base.m;cout<<"the base operator ="<<endl;return
  12.  
  13. *this;}
  14. private:
  15. int m;
  16. };
  17.  
  18. class Derive :
  19. public Base
  20. {
  21. public:
  22. ~Derive(void){cout<<"Derived destructor"<<endl;}
  23. private:
  24. int mx;
  25. };
  26.  
  27. void main()
  28. {
  29. Derive X;
  30. cout<<"this is the separtor-----------------"<<endl;
  31. Derive Y = X;
  32. Y = X;
  33. }

运行结果:

Base default constructor

this is the separtor-----------------

the base copy constructor

the base operator =

Derived destructor

Base destructor

Derived destructor

Base destructor





10 派生类函数的调用

在继承的情况下。派生类的作用域嵌套在基类的作用域中,假设不能再派生类中确定名字,就会在外围的基类中朝赵名字的定义。

<1>派生类调用函数是这种原则:先在派生类中查找名字,查到就停止。查不到的就在基类中查找。

<2>静态类型,动态类型

静态类型:是指不须要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态类型仅依赖于包括表达式的程序文本的形式,而在程序执行时不会改变

动态类型:由一个左值表达式表示的左值所引用的终于派生对象的类型。一个右值表达式的动态类型。就是它的静态类型。

<3>对象,引用或者指针的静态类型决定了对象可以运行的行为

因此引用或者指针不能运行派生类中新定义的成员名字。

<4>基类与派生类的名字发生冲突

基类与派生类中有同样的名字的时候。派生类会屏蔽基类中同名的成员。假设非要调用基类的成员,那么就必须显式的调用。如:Base::func();

<5>基类成员函数的屏蔽

基类和派生类有同样名字的函数,可是原型不一样(參数),那么派生类将不能直接调用基类的那个同名的函数。

依据之前的原则,找到名字同样的就不再往基类找了,所以以下的程序是错误的。

  1. class A
  2. {
  3. public:
  4. void fun(){};
  5. }
  6. class B : public A
  7. {
  8. public:
  9. void fun(int){};
  10. private int x;
  11. }
  12. B b;
  13. b.fun(11); //OK
  14. b.func(); //error

11 using

能够在派生类中使用using来改变继承自基类中的成员的级别。前提是派生类对基类的成员具有訪问权限。

  1. //Base.h
  2. #pragma once
  3. #include <iostream>
  4. using namespace std;
  5. class Base
  6. {
  7. public:
  8. Base(void){ n = 100; };
  9. ~Base(void){};
  10. size_t size()const{return n;}
  11. protected:
  12. //private:
  13. size_t n;
  14. int fn(int x){return x;};
  15. int fn(){return 11;}
  16. };
  1. //Derived.h
  2. #pragma once
  3. #include "base.h"
  4. class Derived :
  5. private Base
  6. {
  7. public:
  8. Derived(void){};
  9. ~Derived(void){};
  10. using Base::size;
  11. using Base::fn;
  12. };
  1. //main.cpp
  2. #include "Base.h"
  3. #include "Derived.h"
  4. #include <iostream>
  5. using namespace std;
  6.  
  7. void main()
  8. {
  9. Derived XX;
  10. Base YY;
  11. cout<<XX.size()<<endl;
  12. cout<<XX.fn()<<endl;
  13. cout<<XX.fn(2)<<endl;
  14. system("pause");
  15. }

在这里我们也能够看到假设基类的函数有重载的话,针对同一个using Base::XXX,能够使所用的名字为XXX的基类函数在派生类中得到声明。

真可可谓一次声明,多个使用。





12 friend 跟继承的关系

友元跟继承没有关系。基类的派生类不能被基类的派生类訪问。

友元类的派生类对基类没有訪问权限。





13 引用转换,转换对象

引用转换:派生类对象转换为基类类型的引用

转换对象:用派生类对象转换为基类的对象。这个时候形參是固定的。编译和执行时候的对象都是基类类型对象。派生类的基类部分被拷贝到形參中。

引用转换:派生类对象转换为基类的引用。可是假设赋值的话也是把派生类的基类部分赋值给基类对象。这和指针的切片效应一个道理。

14 基类和派生类的转换

派生类转对象赋给基类也是有条件才干行的,这个条件就是派生类是通过public来继承的。这种话不论是在成员代码还是在用户代码中。都能实现

  1. Derived d;
  2. Base b = d;

假设是通过protected继承的,那么仅仅能在成员定义中使用这种代码。

15 默认的继承标准

类和结构在默认继承指引相反。

<1>当类继承,假设没有预选赛,那么默认为private继承。如果没有预选赛时的结构,它是默认public继承。

<2>此外strut从开始到内部的第一限定词之间定义的默认public修改。但class缺省值是private修改。

C++ primer札记10-继承的更多相关文章

  1. C++primer 练习10.16

    // 10.3.2.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  2. 【C++ Primer | 15】继承的构造函数

    继承的构造函数 子类为完成基类初始化,在C++11之前,需要在初始化列表调用基类的构造函数,从而完成构造函数的传递.如果基类拥有多个构造函数,那么子类也需要实现多个与基类构造函数对应的构造函数. cl ...

  3. [C++ Primer] : 第10章: 泛型算法

    概述 泛型算法: 称它们为"算法", 是因为它们实现了一些经典算法的公共接口, 如搜索和排序; 称它们是"泛型的", 是因为它们可以用于不同类型的元素和多种容器 ...

  4. java基础(10):继承、抽象类

    1. 继承 1.1 继承的概念 在现实生活中,继承一般指的是子女继承父辈的财产.在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系.例如公司中的研发部员工和维护部员工 ...

  5. C#面向对象10 继承

    1.继承: **** 我们可能会在一些类中,写一些重复的成员.我们可以将这些重复的成员,单独的封装到一个类中,作为这些类的父类. Student,Teacher,Driver ----子类  派生类 ...

  6. 学习C++.Primer.Plus 10 对象和类

    1.类的声明和定义 类的声明和定义. 类声明的格式如下: class className { private://private 是类对象的默认访问控制,因此,可以省略 data member del ...

  7. 对C++ Primer的10.3.9单词转换的思考

    这篇代码有几个知识点可以复习一下,而且小白学到了新知识o(╯□╰)o #include <iostream> #include <string> #include <ma ...

  8. 【C++ Primer 第10章】 1.概述(算法总结)

    泛型算法 find(vec.beign(), vec.end(), val) //返回指向第一个给定值的元素的迭代器 count(vec.bengin(), vec.end(), val) //返回给 ...

  9. 【C++ Primer 第10章】 10.4.2 插入迭代器

    iostream迭代器 标准库为iostream定义了可用于这些IO类型对象的迭代器. istream_iterator读取输入流, ostream_iterator向一个输出流写数据.   1. i ...

随机推荐

  1. win8 远程桌面 你得凭证不工作

    今天在在登录azure远程桌面时,一直提示你的凭证不工作, 按照网上各种教程都不行, 后来发现在win8中用户名结构中,需要加上本机电脑名比如:“win8\administrator”.

  2. MySQL在一台db服务器上面如何启动多个实例

    安装过程省略过,源码安装请参考http://write.blog.csdn.net/postlist/1609043/all 整理自己的文档,发现以前做的例子,share下,欢迎大家提出改进意见. 一 ...

  3. Hadoop Hive与Hbase关系 整合

    用hbase做数据库,但因为hbase没有类sql查询方式,所以操作和计算数据很不方便,于是整合hive,让hive支撑在hbase数据库层面 的 hql查询.hive也即 做数据仓库 1. 基于Ha ...

  4. Ajax改动购物车

    1.购物车类的设计 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hpeWVxaWFuZ2xkaA==/font/5a6L5L2T/fontsize/4 ...

  5. 服务器编程入门(10)TCP回射服务器实现 - 并发

    问题聚焦:     在前面我们大概浏览了一下服务器编程需要掌握的一些知识和技术,以及架构思想.        实践,才是检验真理的唯一标准..从这节起我们将在这些技术的基础上,一步步实现以及完善一个服 ...

  6. STM32串口乱码

    库函数默认8MHz晶振,应根据实际硬件选择 # CMSIS/stm32f10x.h #define HSE_VALUE ((uint32_t)12000000) #if !defined HSE_VA ...

  7. mysql主键设置成auto_increment时,进行并发性能測试出现主键反复Duplicate entry &#39;xxx&#39; for key &#39;PRIMARY&#39;

    mysql主键设置成auto_increment时,进行并发性能測试出现主键反复Duplicate entry 'xxx' for key 'PRIMARY' 解决方法: 在my.cnf的[mysql ...

  8. CentOS 网络设置修改

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G) 系统版本:Centos-6.5-x86_64 路由器网关:192.168.1.1 步骤: 1.查看网络MAC地址 [ro ...

  9. HUNNU11351:Pythagoras's Revenge

    http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11351&courseid=0 Problem des ...

  10. testing and SQA_动态白盒測试

    一.软件測试技术: 黑盒:在不知道程序内部结构,仅仅知道程序结构的情况下採用的測试技术或策略. 白盒:在知道程序内部结构的情况下採用的測试技术或策略. 两种測试方法从不同的角度出发,反映了软件的不同側 ...