c++学习之路----笔记

来源:互联网 发布:百度移动优化排名技术 编辑:程序博客网 时间:2024/05/16 16:04
一.c++简介
   1.1 历史
   80年代  本贾尼.
   83  年正式命名
   87  gnu  c++
   92  微软  c++   IBM
   98  ANSI  c++  ISO  c++98
   03  ISO             c++03
   11  ISO             c++0x
   1.2  c++  和 c 的关系
      c是c++的基础
      c++对类型检查比c要严格 c++是强类型语言
      c++ 扩展了c
         面向对象 (以类的方式来组织代码)
         运算符重载(一种函数的特殊表现)
         异常(一种新的错误处理方式)
         泛型编程(类型通用编程)
   1.3 c++ 的课程安排
      c语言到c++的过渡  2
      面向对象编程      2
      运算符重载        1.2
      封装继承多态      2
      IO 异常           1.8
      
 二.第一个c++程序
    2.1 源代码的后缀是 cpp
        .c  .cc  .C  .cxx .c++   最好是cpp
    2.2 c++程序中的头文件问题
        c++不再以.h作为头文件的结尾
           /usr/include/c++/4.6
           #include <iostream>
           #include <string>
           #include <list>
        可以使用c的标准头文件
           #include <stdio.h>
           #include <string.h>          
        但不再推荐上面的使用方式推荐使用下面的
           #include <cstdio>
           #include <cstring>     
        非标准c头文件 不能使用去尾加头的方式
           #include <pthread.h>
    2.3 输入输出
        cout <<
        cin >>
    2.4 编译器
        可以使用gcc  但需要加链接库
        gcc   ***.cpp   -lstdc++
        最好选g++
        g++   ***.cpp
        
        所有的编译选项几乎一样
        g++  -o  -S  -E  -l  -L -O0 -g (GDB)
             -c  -I  
    
三.命名空间(名字空间)
   3.1 为什么要有命名空间  namespace
       可以对程序进行逻辑划分
       避免命名冲突
   3.2 如何定义命名空间
       namespace 空间名{
           变量定义
           函数声明
           函数实现
             
       }
       同一个命名空间 可以出现多次
   3.3 如何使用命名空间
      3.3.1 在数据前加 命名空间名::   
      3.3.2 使用using 声明
         using  空间名::数据;
         把命名空间下的数据 在当前声明 这样
         就可以在当前作用域直接使用。  
         使用using 可能回引入冲突。
         解决回到第一种.     
      3.3.3 使用命名空间指令
         using  namespace  空间名;
         
         cin  istream
         cout ostream
   3.4 无名命名空间
       如果一个数据没有定义在任何命名空间中
       则这个数据会被自动放入无名命名空间。
       ::数据
       
       namespace {
           数据
           /* 不能跨文件访问 */
       }   
   3.5 命名空间 嵌套
       namespace  ns1{
           int   count=1;
           namespace ns2{
               int  count=10;
               namespace ns3{
                   double  salary=100;
                   void  show(){
                       cout << salary << endl;
                   }
               }
           }
       }
 四. c++ 中的结构 联合 枚举
    4.1 结构体
        定义结构体语法 和 c 完全相同。
        在定义变量时 可以省略struct 关键字。
        c++中的结构体中 可以定义函数。这些函数
        称之为成员函数。         
    4.2 联合体
        共享内存
        突破编译器的一些限制
         
        定义联合体的语法 和 c  完全相同。
        在定义变量时 可以省略union 关键字。
        c++ 支持匿名联合体。
        union{
            int  x;
            char data[4];
        };
    4.3 枚举
        定义枚举类型 和 c 完全相同。
        定义变量时  可以省略 enum 关键字。
        枚举值 能不能赋值给整数变量,整数可
        不可以赋值给枚举?
        c中后半句可以 c++ 不允许 这里体现了
        c++的强类型。
 五. c++ 中的bool 类型
     c99  #include <stdbool.h>
     c++ 中直接值bool 类型
        true  1  false  0
     bool类型可以赋值任何值 但下面的四个值表示
     假 0 '\0' NULL  false
        
 六.c++ 符号替换
    a&&b   a and b
    <%       {
    %>       }
    
    %:       #        
    bitand   &
    
 七.c++ 中的函数
    c++中函数 无参代表没有任何参数。
        void依然可用。   
    c++的函数调用前 必须前置声明,不支持
        c语言中的隐式声明。
    c++的函数默认不再返还int  函数必须设计
       返回值类型。如果没有返回值则设计成void。
       main函数除外。    
八.函数重载
    8.1 概念  overload
    在同一作用中 函数名相同 参数列表不同的函数
        构成重载关系。   
    参数列表不同:参数类型 参数的个数
                 参数的顺序
                 int  double
    8.2 举例
    int   add(int x,int y);
    double add(int x,double y);
    double add(double x,double y);
    double add(int x,int y,double z);
    8.3 函数指针 调用这些函数没有什么问题
    因为函数指针 会根据函数指针的类型来确定
    函数地址。
    double  (*pfun)(double x,double y);
    double (*padd)(double x,double y);
    8.4 函数重载的原理?
       c的函数在进行编译时 编译器只考虑函数的
       名字。
       c++中 的函数 进行编译时 生成函数名 不但
       要考虑函数名 而且会考虑参数列表。
       
       extern "C"  告诉c++ 编译按照c的方式来生成
       调用函数名。
九.函数的哑元  
   9.1 概念
   一个函数的参数 只有类型 没有形参名 这个参数
   称之为哑元。
   9.2 作用
   让调用更严格
   void  show(void);
   
   函数向前兼容
   void  encode(int key);
   void  decode(int key);
   
   void  encode(int key);
   void  decode(int);
   
   函数区分
   Date  date;
   ++date;
   Date& operator++(){
       /* 这个函数代表前++ */
   }
   date++;
   Date operator++(int){
       /* 这个函数代表后++ */
   }
十.函数参数的默认值
   10.1 如果一个函数的参数 指定了默认值  则调用
   这个函数 不给这个参数赋值就使用默认值,如果
   给这个参数赋值 则替代掉默认值。
   10.2 作用
   参数的默认值 必须靠右
   参数的默认值 不要和重载构成冲突
   int   getmax(int x,int y=123,int z=100);
         drawImag(int x,int y ,QImage&  img,
             int x=0,int y=0,
             Qt::ImageConversionFlags flag=
             Qt::AutoColor);
   getmax(200);
   getmax(1);
   getmax(1,2);
      
   减少函数的个数
   方便调用者调用
   10.3  设计一个函数  打印整数数组  默认打印
   前5个数据  可以指定数据的分割分号 默认以
   逗号分割。
   [9,6,8,3,2]
   
   参数的默认值 不要和重载构成冲突
   
十一. 内联函数
   11.1  内联函数在函数调用的位置  直接把函数的
   二进制代码复制过去。
   inline void    getmax(int x,int y){
   
   }
   11.2 内联函数 在调用时 省了开栈 清理栈的时间
   开销,提高了执行效率。但会造成 代码体积变大
   内存消耗也会变大。
       函数体积大 稀少调用 不适合内联。
       体积小     频繁调用  适合内联。
       递归函数 无法实现内联。
   内联 只是给编译器的一个建议,建议成功 则是
   内联形式调用,建议不成功 就是普通函数调用。
       
十二. c++ 中的动态内存分配
    12.1 c 中的动态内存分配
    malloc   calloc  realloc   free
    c++ 中使用
    new    delete
    new[]  delete[]   
    12.2 语法
    /* 申请一个类型大小的内存 */
    类型  *指针名=new 类型;
    类型  *指针名=new 类型(值);  
      
    /* 释放指针对应的内存 */
    delete   指针名;
    12.3  写一个Date类型(年 月 日)结构体
    然后使用 new  申请一个结构体大小的内存
    然后给这块内存赋值  并输出这块内存中的
    值 最后释放这块内存。
    
    
    12.4 使用 new[]  和 delete [] 申请和 释放
     多个对象所占的内存。
    类型 *指针名  = new 类型[n];
    
    delete[]  指针名;
    
    申请5个整数的内存空间 然后操作这块内存
    最后释放内存。
    
    12.5 定位内存分配 (了解)
       char  data[100];
       int *pa =new (data)int[25];
       在一个已有的内存上 分配内存, 这个指针
       对应的内存不用释放。
    
 十三. 引用  (references)
     13.1 概念 (什么是引用?)
     引用既别名
     9527   华安   唐寅   唐伯虎
     机器猫  小叮当   哆啦a梦
     13.2 语法
     引用必须初始化
     int  a=100;
     int& ra=a;
     引用一旦初始化 引用的对象不能更改
         终生为这个对象服务。
     const 修饰的引用 可以赋值常数
     const int& rb=100;    
     
     13.3 引用作为函数参数(引用传递)
         值传递
         swap(int x,int y);
         swap(int *x,int *y);
         引用传递
         swap(int& x,int& y);
         
         引用传递注意的问题
             如果函数内部不对参数进行修改
             尽量对引用类型的参数加const
     13.4 引用作为函数的返回值
         函数的返回一般用来做右值。
         int  x=getmax(a,b);
         getmax(a,b)=100;
         
         如果希望函数返回值做左值 可以使用
         引用类型。
         不能返回局部变量的引用 可以返回
         全局  堆 函数参数 static 成员函数
         中返回成员变量的引用 。不能返回
         局部变量的引用。
     13.5 引用如何是如何实现的
         引用是用指针来实现的
         int   a=100;
         const int* pa=&a;
         *pa=200;    
         
         大体如下:
         int *const newra=&a;
     13.6 指针 和 引用 区别和联系?
          
 十四.c++ 中类型转换运算符
     static_cast<类型>(变量);
         在某一个方向上可以 做隐式类型转换的
         转换可以使用静态转换。
         int   *pi;
         void  *vp=pi;        
     dynamic_cast<类型>(变量)
         具有多态性的父子类之间 转换使用。
         
     const_cast<类型>(变量)
         去除const修饰时的转换
            
     reinterpret_cast<类型>(变量)                      
         重新解释的内存转换  最接近强制类型的转换
    
     int&  实现了 一级指针的效果
     如果要实现二级指针的效果 如何用引用实现?
    
 十五.c++ 之父给c程序员的建议
    c++编程中 尽量少使用 宏
        const,enum 定义常量
        使用inline 替代带参宏
        使用namespace 避免命名冲突
    变量随时用随时定义 并保证初始化
    尽量使用 new delete 运算符去分配和释放内存
        少于malloc  free
    尽量少用c风格的字符串 使用c++中的string
    尽量不要做强制类型转换,非要强转 也要使用
        c++ 提供的转换运算符。
    逐步建立面向对象的思想
    (以类的方式来组织代码)
    
                  面向对象编程
一.什么是对象?
   一切皆对象
   一个程序就是有一组对象组成的一个整体,程序的
       功能通过对象和对象之间发送消息完成。
   每个对象都有一个类型。
   同一个类型都能接收相同的消息。
   
   现实世界         抽象          计算机世界
   Person           
   张三(英雄)                      英雄(打人)
   age
   power
   
   李四  
   
   具体 -----        抽象
   具体的人          学生  
   具体的人          咨询
   具体的人          老师
二.如何描述一组对象   
   抽取共同(主要)特征       汽车
   name                     (销售)    (维修)
   age                      品牌      型号
   gender                   价格      车主
   address                  颜色      
                            油耗
       变量
   抽取共同的功能
       函数    
 三.一组学生的共同特征  和  功能
    学号              吃饭     
    姓名              睡觉
    年龄              玩
    学校              学习
    班级              
 四.如何描述 这组对象的类型
   4.1 使用结构体描述      
   使用结构体 描述一个时间  提取时间的特征
   和时间的功能。
   特征:
   小时   int  hour;
   分钟   int  min;
   秒     int  sec;
   功能:
   时间的显示
   设置时间
   走秒 (调用一次 秒数加1)
   运行(一秒钟 显示一次  不断循环)
   
   
   
   4.2 使用类(class)描述 类型
      class中默认的权限是私有的,而struct中
      默认的权限是公开的。
      
      权限修饰:
      public:  数据可以在类内和类外访问
      private: 数据只可以在类内访问
      protected: 类内 和 子类中可以访问
五.构造函数
   5.1 什么是构造函数
   是一个特殊的函数 和类型名相同。
   没有返回值类型。
   能保证创建一个对象时 自动调用一次。
   构造函数作用是 初始化对象。
   如果一个类型不提供构造函数 则系统自动提供
       一个无参的构造函数。但一旦人为提供构造
       函数则系统提供的无参构造自动消失。      
   5.2 一个对象创建的过程
      根据对象大小 分配内存。
      如果类的成员变量是基本类型 则什么都不做
          如果是类类型的成员 则去构造它。
      调用这个类型的构造函数。                    
   5.3 初始化参数列表
      当类型中有 const成员 或者引用类型成员时
      需要在构造函数调用之前赋值。
      在构造函数参数列表之后 实现体之前的位置
      是初始化参数列表。
      class A{
      public:
      A():/*这里就是初始化参数列表*/{
      
      }
      };
      
 六.实际开发中  类的头文件 和 实现文件是分离的
    头文件:
    头文件中是对类型的定义
    class  类型名{
        成员变量定义;
        成员函数的声明;
        构造函数声明;
    };     
      
    实现文件:
    对成员函数 和 构造函数进行实现。
    如果函数有参数的默认值 在头文件中指定
    实现文件中不能指定。
    
    
    写一个日期类  有三个参数的构造函数 默认值
    是2014年5月8日。要求可以显示日期  还可以
    设置日期 日期的默认值是 2014年1月1日 。   
    还要求 头文件 和实现文件分离。
 
一. this 指针
    1.1指向当前对象的指针。
       在构造函数中 this 代表指向正在被构建的
       对象。
       在成员函数中 this 代表调用这个成员的
       对象。  
    1.2 this指针的使用
       可以函数参数的名字 和 成员变量重名时 做
       一个区分。
       可以作为函数的返回值。
       可以作为函数的参数。
二. const 对象 和 const函数
   2.1  A   a;       
        const  A  aa;
        class A{
            public:
            void   show(){
                /* 普通成员函数 */
            }
            void   show()const{
                /* 这是const函数 */
            }
        };   
   2.2 const 对象 只能调用const 函数。
       const 函数和非const 函数可以形成重载。
       非const对象 优选调用 非const 函数,如果
       没有相应的非const 函数则调用const函数。
       
       void  testa(const A a){
           a.show();
       }  
       const 函数中只能读 成员变量 如果需要
       修改成员变量 可以对成员变量加一个mutable
       修饰.
三.析构函数
   3.1 什么是析构函数
   一个特殊的函数  和类名相同 但函数名前有一个~
   ,不能有任何的参数,没有返回值类型。
   这种函数 可以在对象销毁前 自动调用一次。
   如果你想调用多次析构函数 理论上是可以的。
   3.2 举例
       new   delete
       new   delete[]     
   3.3 什么时候 需要自定义的析构函数
      对象销毁时 释放资源。
      一般有动态内存 分配时   
   3.4 在构造函数中 或者初始化参数列表中为
      指针类型的成员变量 分配动态内存。
           
      在析构函数中 释放掉 动态分配的内存。
 
 四.拷贝构造函数
    4.1 拷贝构造函数 是一个特殊的构造函数
        是用一个已经存在的对象 去创建另外
        一个同类型的对象。
        class A{
           public:
           A(){
           
           }
           /* 拷贝构造函数 */
           A(const A& a){
           
           }      
        };
     4.2 拷贝构造函数调用的时机
        使用同类型的对象 创建另外一个对象。
            A   a;
            A   b=a;
        把一个对象 赋值给函数的形参。
            showa(A  a);
        把一个对象 作为函数的返回值。
            A  geta(A  a){
               return  a;
            }    
     4.3 什么时候需要 自定义拷贝构造函数
         希望自定义对象拷贝过程。
         需要让对象 有自己独立内存时。
                
         默认拷贝构造函数 是把原对象的数据
             进行逐字节拷贝。   
     4.4 其它发生拷贝的情况
        作为函数参数
        
        作为函数返回值
                
 五.静态函数  和 静态成员变量
    5.1 静态函数是不需要对象就可以直接调用的成员
        函数。
    5.2 静态受类名作用域 和 权限的控制。
    5.3 静态成员函数 只能访问静态成员 不能
        (直接)访问非静态成员。     
    5.4 普通的成员函数中 编译器负责传入了一个
        指针this,而静态函数没有传入this指针。           
    5.5 静态成员变量 必须在类外初始化。
        class A{
            static  int  a;
        };        
        静态变量的类型  类名::变量名;
        如果静态成员是基本类型  则赋值成 0
        如果是自定义类型 则自动调用无参构造。
        int   A::a;
                
   /* 单例模式
      写一个类 这个类只允许在一个进程中
      创建出一个对象。
      权限   构造函数  拷贝构造函数
      静态成员变量  静态函数  引用
      */          

    

一. 成员指针
    1.1 指向成员变量的指针
    1.1.1 语法
    struct Date{
        int   year;
        int   month;
        int   day;
    };  
    int  Date::*pm;
    成员变量的类型   类名::*指针名;
    pm=&Date::year;
    1.1.2  调用
    Date  date;
    date.*pm;
    Date  *date2 =new  Date();
    date2->*pm;
    
    1.1.3 成员变量指针的本质 是这个成员变量
      在对象中的地址偏移量。
      
    1.2 成员函数 指针
      struct Date{
        int   year;
        int   month;
        int   day;
        int   getYear(){
           return  year;
        }
        int   getMonth(){
           return  month;
        }
       };        
    
       int   (Date::*pmfun)();
       pmfun=&Date::getMonth;
       (date.*pmfun)();
       (指针对象->*成员函数指针名)();
      成员函数指针的值 就是成员函数在代码区的
      绝对地址。
      
      int (*p)[5][4]=new  int[3][5][4];
      int (*pa)[7]=new  int[4][7];
 
 
 
      
 二.运算符重载
    2.1 本质
       函数的特殊表现形式。
    2.2 写一个类  表达分数
       特征
           int  x;
           int  y;
       功能:
           显示分数
           加法
           减法
    2.3 Fraction  fa(1,2);
        Fraction  fb(1,3);
        当编译器  翻译到 fa+fb时
        先去Fraction类型中找一个成员函数叫
        operator+(const Fraction& fb)
        如果没有这个形式的成员函数  则去
        全局找一个函数 operator+(
        const Fraction& fa,const Fraction& fb);
        
        a#b    operator#(b)
               operator#(a,b)
        
   2.4 写一个成员函数operator- 负责两个分数相减
       写一个成员函数operator* 负责两个分数相乘
   2.5 访问权限的解决
        为private 的成员,提供访问接口
        可以把函数 声明成友元函数。
        (获得了访问类私有成员权利的全局函数)
        必须在类内声明:
        friend Fraction operator*(const Fraction& fa,const Fraction& fb);
        
        
        成员函数:
            访问类私有成员的权利。
            作用域位于类内,受访问权限控制
            必须通过对象访问
            
        普通成员函数具有以上三个特性:
        静态函数只具有前两个。
        友元函数只有第一个(本质上就是全局函数,只不过获得了访问私有成员的权利)
        
    2.6  包装一个int 类型 Integer类型
    
    
    2.7 特殊的二元运算符号
        
        cout << b;
            先去cout对应的类型ostream 中找一个成员函数 operator<<(b)
            如果找不到 就去全局找一个函数
            类库中必须用引用,必须加const修饰。不允许拷贝。
            operator<<(ostream& os,b):
            
        cin>>b
            先去cout对应的类型ostream 中找一个成员函数 operator>>(b)
            如果找不到 就去全局找一个函数
            类库中必须用引用,必须加const修饰。不允许拷贝。
            operator>>(ostream& is,b&);
            
        
    2.8 写一个Integer 类封装整数,使用成员函数形式,来实现 + - * /
        然后提供输入输出运算符,实现比较两个整数是否相等的运算符实现 == 。
        再实现一个运算符 -= 。     
   2.9  一元运算符重载
          -  !  ++  --
          Integer a;
          -a;
          #a  去成员函数中找一个成员函数叫 operator#() 如果找不到就去全局找一个
          operator#(a);         
   
   
三、指针和引用的区别与联系
    联系:
    引用本质上就是指针,指针的大部分效果都可以使用引用来实现
        指针作为函数参数
        ymswap(int* x,int* y);
        myswap(int& x,int& y);
        
    二级指针作为函数参数就是希望在函数内部,修改指针的指向。
        changePTR(int** ptr);
        changeptr(int*& prt);
        
    指针作为函数的返回值


    区别:
        1 引用定义时,必须初始化,指针没有这个要求
        2 引用一旦初始化,就不能改变引用的对象。指针可以更改指向。
        3 指针是一个实体变量,大小永远是4,引用是一个别名 大小和引用的对象有关。
        4 有指针的指针(二级指针),没有二级引用
        int **pi;
        int &&pi;    //error
        5 有指针的引用,没有引用的指针。
        int*& pa = 指针;
        int&*    error
        6 有指针的数组
            没有引用的数组
            
        7 但有数组的引用
            int data[3] ={1,2,3}
            int (&data)[3];         
    

一.运算符重载中的限制
   1.不能重载的运算符号
   ::  .   .*   sizeof  typeid   ? :
   typeid  取得类型信息的
   2.不能发明运算符 只能已经有的运算符重载
     $  #
   3.不能对基本类型的数据进行重载
     至少有一个是类类型
   4.不要改变运算符的特性
   5.只能重载成 成员的运算符
     =  最好是成员的 += -= /= %=
    []      
    ()
    ->  *
     
二.只能是成员的运算符
   2.1  =  []     
       写一个自定义数组 Array  
   2.2 ()
       单参的构造函数 允许把这个单参类型
       转换成 构造函数所对应的类型。
       防止这种意外转换 可以使用 explicit
       
       ()运算符 也可以做类型转换
        operator 转换成的类型 (){
       
        }
       () 函数对象
        可以像函数一样 使用一个对象
        返回值类型 operator()(参数类型 形参名,
            参数类型  参数名){
        
        }
   2.3  ->   *
       把一个不是指针的类型 当做指针类型来使用
       (智能指针 原理和简单使用)    
       
总结:
    双目运算符
    a#b   首先去a对象对应的类型中找一个成员
    函数叫  operator#(b).如果找不到就去全局
    找一个全局函数 operator#(a,b)       
    单目运算符
    #a    首先去a对象对应的类型中找一个成员
    函数叫  operator#().如果找不到就去全局
    找一个全局函数 operator#(a)
    a#    首先去a对象对应的类型中找一个成员
    函数叫  operator#(int).如果找不到就去全局
    找一个全局函数 operator#(a,int)
    
    权限不够时:
    公开接口
    友元  (访问私有成员
           <<   >>  流对象不能复制 不能const)  
 
    new       void*  operator new(size_t size);
    delete    void  operator delete(void* ptr);
      
       返回值    operator#(参数列表)
  -------------------------------------------     
   volatile const  int  a=100;
   const  int *pa; 不能通过pa修改值  
       但可以改变指针的指向。
   int *const  rpa; 可以通过rpa修改值 但
       不可以改变指针指向。
   int const*const  ptr;
   
   const  int& ra=1000;
   函数参数   函数的返回值  
   void  showa(const A*  a);
   void  showb(const A& a);
   const 对象 和 const函数的关系
   mutable
   
   const 返回值  函数名(const 参数)const{
   
   }
   
   cout << arr.size  << ":" << arr.len
   
一.面向对象的三大特征
   封装
   继承
   多态

二.封装
   该隐藏的就私有化  该公开就公开化。
   private:           public:  
   便于分工 和 协同开发
       加密
         const char*  code(const char* msg,
         int  key);
       解密
         const char*  decode(const char*msg,
         int  key);
   防止不必要的扩展
       class  A{
           public:
           list  getData();
           private:
           void  fa();
           void  fb();
           void  fc();
           void  fd();
       };        
三.继承
   3.1 作用
   代码 和 数据的复用
   在已有代码 和数据上 扩展
   3.2 语法
   class  A{   };
   class  B:public A{   };
   
   class Animal{   };
   class Dog:public Animal { };
   class Cat:public Animal { };
    
   子类  is  a  父类
   Dog   is  a  Animal;
   Cat   is  a  Animal;
   
   3.3  另一种代码复用方式
      组合
   Dog    has  a  Animal;
   汽车   has  a  发动机;
   汽车   has  a  收音机;                        
                  
   3.4  继承的方式
      公开继承   class A :public B{   };
      保护继承   class A :protected B{  };
      私有继承   class B :private B {  };
   3.5 公开继承下 父类数据 到子类之后的权限变化
      在公开继承下  父类的公开的数据 到子类之后
      是公开的。
      父类中的保护的数据  到子类之后是保护的。
      父类中的私有数据    到子类之后是隐藏的。                     
      
   3.6 保护继承
      在保护继承下  父类的公开数据 到子类之后
      是保护的。
      父类保护的成员 到子类中是保护的。
      父类中私有的成员 到子类中是隐藏的。    
   3.6 私有继承
      父类的公开数据 到子类之后
      是私有的。
      父类保护的成员 到子类中是私有的。
      父类中私有的成员 到子类中是隐藏的。
    所谓的继承方式 就是能提供给子类的最大访问
    权限。实际的权限小于等于这个继承方式。
    私有的数据 到子类中一定是隐藏的。        
      
四.继承中构造 和 析构的调用
   构建子类对象时 一定会调用父类的构造函数。
   析构函数的调用顺序 和 构造函数调用的顺序
   相反。
   子类默认调用父类的构造函数,当然也可以指定
   调用哪个构造函数。在初始化参数列表中 指定。
五.子类不能继承 父类的构造函数 析构函数 赋值
   运算符函数  拷贝构造函数。但可以调用父类的
   这些函数。
     
六.名字隐藏  (name hide)
   子类继承了 父类数据之后 如果提供了 和 父类
   同名的数据。则会把父类的数据隐藏掉。
七.多继承
       
   一.多继承
   1.1
   一个类 可以多个父类  构建这个对象的顺序
   和这个类 继承类的顺序相关。
   如果继承的数据 不产生冲突 则可以直接调用,
   如果产生冲突可以使用 父类名作用域区分,
   使用名字隐藏机制 也可以解决。
   1.2 多继承代码的优化
   把父类中的 共同成员 抽取出来
   double    price;
   double    getPrice(){   }
   放入一个更高层的类中  
   然后使用 虚继承 继承最高层的类
   对直接子类而言 和 普通继承的区别 只是
   多出了一个指针 维护虚继承关系。
   对子类的子类而言 不在从父类中拷贝 最高层
   类的数据,而是直接访问最高层的类 和 最高
   层类的子类 没有什么区别。但要付出维护虚
   继承的代价。
二.虚函数
   虚函数 是在普通成员函数上 加了virtual 修饰。
   一个类型 如果有序函数 则编译器提供一个指针
   指向这个类型的虚函数表。虚函数表中的元素就
   是每个虚函数的地址。
   这个指针的值存入对象的前四个字节。  

三.对虚函数的重写(over write)
   函数重写: 是针对虚函数 重写要求函数名相同
   参数列表相同 返回值相同。   
   名字隐藏: 在子类中 出现和父类同名的数据。  
   函数重载:(over load) 同一个作用域 函数名
   相同 参数列表不同。
四.多态
   4.1 概念
   当父类对象的指针(或引用) 指向 子类对象时
   调用父类中提供的虚函数 表现将会是子类中
   函数覆盖的实现。
   4.2 多态的基础 是继承
       虚函数是多态的关键
       函数覆盖是必备条件
   4.3 写一个动物类  Animal  有一个虚函数 run
       还有一个虚函数  fun
       还有一个非虚函数 show  
       写一个子类 Cat  覆盖run 和 fun  show
       再写一个子类 Fish 覆盖run 和 fun show
       然后使用 Animal 指针 分别指针 Cat 和
       Fish类型的对象 调用 fun run show
       观察表现。  
   4.4 多态的使用
       作为函数的参数
       可以做到类型的通用
       可以在函数内部 根据实际对象类型做出
           相应的动作(虚函数)
       函数的返回值
                    
   4.5 多态的原理
       虚函数表指针
       虚函数表
       
       每一个类型都有自己类型对应的虚函数表。
       同类型的对象共享虚函数表。
       子类和父类的虚函数地址不同。     
        Animal  *pa
        if(a==1){
            pa=new Cat();
        }else if(a==2){
            pa=new Dog();
        }
        pa->run();
       
   4.6 类型上通用,方便了编程。但同时损失了
      对象的一些个性。但有时我们需要识别
      出对象真正的类型 进而恢复对象的个性。
      具有多态性的父子之间
      dynamic_cast<类型>(对象)
      必须具有 虚函数 转换成功 就返回一个
      合法地址 转换失败 就返回NULL。   
       
      typeid  获取一个对象 或者类型的 类型信息
      #include <typeinfo>
      type_info typeid(对象|类型)
      name()   得到类型的名字
      ==       判断两个类型的type_info 是否
               一致就是同一类型。
      !=       判断两个类型的type_info 是否
               不等。
      如果一个父类对象的指针 指向子类对象时
      一定要确保父类型中有虚函数,如果没有
      虚函数 则会把指向的对象 识别成父类类对象。
      
 五.虚析构函数
    当父类对象指针 指向子类对象时 如果释放
    对象空间,则会调用父类的析构函数不会调用
    子类的析构函数。
    当父类型中出现 虚函数时 则应该在父类的
    析构函数前加virtual修饰。
 六.抽象类
    一个类中有 纯虚函数时 则这个类就是抽象类。
    抽象类不能被实例化,初次之外和正常的类没有
    任何区别。
    class A{
        public:
        /* 这就是纯虚函数 */
        virtual void  show()=0;
    };        
    如果子类没有实现 纯虚函数 则子类成为抽象类。
    
    纯抽象类:除了构造和析构之外 其它所有的函数
    都是纯虚函数。
    
    
一.异常
   1.1 为什么要有异常 和 异常处理
      之前表达错误 都是用返回值
      c++可以使用 异常表达程序的状态
   1.2 如何表达异常
      通过  throw  值;
            throw  对象;
      当异常发生时  程序的默认处理是 程序终止。
   1.3 如何捕获异常
      在可能出现异常的代码上  加
      try{
         foo();     
      }catch(类型  变量名){
          
      }catch(类型  变量名){
          
      }catch(类型& 变量名){
          
      }
   1.4  异常 在函数上的 说明
      /* 这个函数可能会抛出任何异常 */
      void foo();
      /* 这个函数调用时 可能会抛出int
         型 或者double型 或者const char* 型的异常 */
      void foo()throw(int,double,const char*);
      /* 不抛出任何异常 */
      void foo()throw();
   1.5 系统预定义异常
      都是 exception的子类
      在调用系统函数时 或者 某个系统类的成员
      函数时 可能会触发系统定义的异常。则
      抛出哪个异常就处理哪个异常。
      需要导入头文件
      #include <stdexcept>
             
   1.6 用户自定义异常
      定义异常 (项目详细设计中已经设计好)
          定义一个类表达异常
      根据条件 抛出异常
          在函数中 根据条件抛出
          在函数声明上指定 抛出哪些异常
      捕获异常
          在调用函数的代码上 加
          try {
              
          }catch(){
              
          }catch(){
              
          }
      处理异常
          根据异常的类型 做出不同的处理
           
 二.IO
    2.1 IO流
    cout
    clog
    cerr
    cin
    2.2  使用IO流中的头文件
    cin  cout ------#include <iostream>
                    istream  ostream  
    字符串流  ------#include <sstream>
                    istringstream ostringstream
                          
    文件流   -------#include <fstream>
                    ifstream   ofstream
                           fstream    
         
   2.3  非格式化IO    
      get
      put
      getline
      ignore
      clear
           ifstream  ifs("test.txt");
           ifstream  ifs2;
           ifs2.open("test.txt");
      peek  
   2.4 字符串的操作
      c中   字符串常见的操作函数
            sprintf
      c++   中字符串的操作
         string    abc("./a.out ");
         abc+=" a";
         abc+=" b";
      istringstream  数据来自于字符串  
      ostringstream  把数据存入字符串
      构造函数 无参构造即可 使用string构造
               使用c风格字符构造
      运算符  <<   >>
      如何把流对象 变成string  
   2.5 文件的操作
      istream& read(char* buff,size_t size);
      ostream& write(const char* buff,
          size_t size);
      gcount()  得到实际读取到的字节数
      
      写一个程序 读取一个文件 然后把读取的每个
      字符和一个随机的字符(0-255)做异或运算。
      把异或之后的数据写入一个文件。
      再提供一个函数 传入一个 0-255 之内的
      字符 用来解密。
      ./a.out   a.txt   b.txt  
      ./a.out   a.txt   b.txt   key
      ./a.out   a.txt

0 0
原创粉丝点击