C++笔记 4

1、类和对象
   类就是对对象的描述,主要从属性和行为两个方面描述。
   对于属性一般作成private , 行为作为public
   函数 (1)构造函数,初始化所有的成员变量,系统自动调用,可以重载
        (2)析构函数,在对象生命周期结束的时候自动被调用调用,不准重载
               构造函数和析构函数都是系统自动调用的,析构函数可以通过对象调用  
               A a;
               a.A();   //error 构造函数是不能手工调用的
               a.~A();  //right 手工调用析构函数时,会被当作一个普通的成员函数调用,其中的代码会被执行,对象不被销毁
        (3)get,set方法  用于访问私有的成员变量的,外界访问变量的唯一通道
        (4)类本身的行为 是我们在编码时要下功夫的地方
2、类的组成
   (1)数据部分
   (2)构造函数和析构函数
   (3)get & set方法
   (4)业务方法
  
3、栈的实现(存储int类型的栈)                          
   要想实现,需要一个数组做容器,保存用户插入的数据,用指针实现; int *p;
             还需要一个变量,保存栈顶位置   int top;   //top从0开始
             对于栈不能无限制的存数据,所以需要一个int类型的变量来记载数组的最大长度   int max;
   (1)构造函数
        当不给参数的时候,栈的长度是默认值Stack();也可以用户指定长度Stack(int)
   (2)析构函数
        一定要有,因为要在堆中申请空间
   (3)为保护栈数据的安全,对p不能提供get,set 方法
        对于top,max的值,取决于插入的数据,所有只能有get方法,不能设置,不需要有set方法
   (4)int push(int);  插入,当栈满的时候,返回-1,表示插入失败
        int pop();      删除栈顶的元素,如果正确删除一个数据,返回数据的值,若没有正确删除,返回-1,表示栈空了
        void disp();    现实数据
   (5)实现的时候,注意:
        在函数名前加 " Stack:: "
   (6)在栈中插入数据,就是在top的位置插入数据
              删除数据,就是把栈顶下移一个位置
             
4、继承
   (1)继承表现一种" is a " 的关系。Teacher extend Person
        其中Teacher是子类,Person是父类
        子类继承父类的所有属性和行为
   (2)class Teacher:public Person{};
         表示Teacher类继承Person类       
  
   在extends_sample2中,子类可以直接使用父类的teach函数,但是不能使用父类private的name属性。name属性实际上存在在子类中,但是不能被子类直接使用,因为是private属性。private属性只能被本对象的函数访问,public的属性可以被子类直接使用,但是外部函数也可以访问public权限的属性。既想被子类使用又不想让外部函数访问,怎么办哪?使用protected修饰属性就可以了。

     下面是一个类的属性具有不同的访问控制修饰符在不同的对象中的可见性i(可见性就是可以访问):   
                 本对象  子类对象  其他函数
 private属性      可见    不可见     不可见
 protected属性    可见     可见      不可见
 public属性       可见     可见       可见


    在继承关键字extends前面也有三种不同的访问控制修饰符号,被不同的继承方式继承后访问控制权限会发生变化。可以把继承方式理解成城门。无论外边的人多么胖,想通过不同宽度的城门只能减肥到相应的宽度才可以。

                      public extends       protected extends       private extends
父类的private属性       不能访问               不能访问                  不能访问
父类的protected属性  变成protected                 不变             变成private,子类可以访问,子类的子类不能访问
父类的public属性          不变               变成protected          变成private,子类可以访问,子类的子类不能访问


    构造函数不能被继承。因为构造函数只是适合于本类的产生方式。

    如extends_sample4,创建子类的时候需要首先创建父类。怎么理解哪?
    考虑子类的构成,就像cs中的匪徒用的46是由ak47添加上瞄准镜组成的。

    创建子类的时候会首先调用父类的构造函数,因为继承的时候没有指定继承时使用的父类的构造函数。
    构造函数有很多种,因为没有指定构造函数,就会默认使用无参的构造函数。如果父类没有无参的构造函数,那么就会出现编译错误。

    这是问题的产生,如何解决哪?
    可以在父类中添加无参构造函数。如果我们不是父类的设计者,就应该在子类继承的时候指定使用父类的那个构造函数。
    如在写子类构造函数时,使用这种形式Teacher(char* name, int age, double salary):Person(name,age){......},就可以指定使用父类的有参构造函数。

    构造时候是先父类后子类。析构函数哪?析构函数不能被继承。子类释放的时候,首先调用自身的析构函数,再调用父类的析构函数。这与构造函数的调用顺序相反。

5、在子类中可以修改父类的行为,叫方法的覆盖
   (1)在子类中的函数名必须与父类的一样
   (2)隐藏,无法产生多态
        class Base{
        public:
           void fn( int a ){
              cout<<"Base a = " << a << endl;
          }
        };

        class Sub:public Base{
        public:
           void fn(  ){
              cout<<"Sub b = 20" << endl;
           }
        };
    (3)调用父类的方法
          a.Base::fn(10);  //可以有针对性的调用父类的函数
         
6、函数的高内聚,低耦合
   高内聚:函数的功能尽量单一,这样的代码可维护性高
   低耦合:避免修改函数的时候,产生连锁反映。
  
7、多态
   (1)什么是多态?
        一个int类型的指针,只能指向一个int类型的变量
        对于对象来讲Person类型的指针 ,能指向Person类型的对象
        Person类型的指针能指向一个Teacher(extend Person)类型的对象
       
   (2)多态的特征:
        父类的指针可以指向子类的对象
        通过父类指针只能调用父类中声明的方法
        通过指针调用函数的时候,若函数是个虚函数,表现出来的行为是对象自身的行为
       
        Person *p = new Teacher ;
        编译时类型     运行时类型
       
   (3)子类的指针不能指向父类对象
        因为子类中会有子类特有的函数,运行时不能通过指针调用子类特有的函数
       
   (4)" virtual "父类函数的返回值前加此关键字,则为虚函数
  
   (5)产生多态的必要前提:
        继承,方法覆盖,虚函数
       
8、虚函数的实现原理
  在每个对象中都有一个虚函数列表的指针,虚函数列表是一个栈。
  在构造对象时,根据先构造父类,再构造子类的原则,父类的函数先入栈,在子类中覆盖的函数放在上面。
  等对象构造完毕时,子类的函数在最上面,根据栈后进先出的原则,先调用的是子类函数

9、在释放资源的时候
   delete p ;
   只会调用Person类的析构函数,因为指针的类型是Person的,这样会造成子类的空间得不到及时的释放,会造成内存泄露
   把析构函数也写成虚函数,这样就会先调用子类的析构函数,再析构父类的析构函数
  
   在继承关系中,父类的析构函数必须是虚函数!!!       
                      
10、多态的使用
                   #include <iostream>
  using namespace std;
  
  class Person{
  public:
   virtual double buy(){         
    return 2 ;
   }
  };
  
  class Teacher : public Person{
  public:
   virtual double buy(){
    return 1 ;
   }
  };
  
  class Student : public Person{
  public:
   virtual double buy(){
    return 0.5 ;
   }
  };
  
  class CEO : public Person{
  public:
   virtual double buy(){
    return 1000 ;
   }
  };
  
  void shoufei( Person * p ){
   cout<< p->buy() << endl;
  }
  
  
  int main(){
   Person p ;
   Teacher t ;
   Student s ;
   CEO c ;
  
   shoufei( &p ) ;    //通过传入不同对象的地址,调用相应的函数
   shoufei( &t ) ;    //与if...else对比
   shoufei( &s ) ;    //写程序要尽量少改动写好的源码,这样实现代码的通用及低耦合
   shoufei( &c ) ;
  
   return 0 ;  
  }                              
             
作业:写一个校车类,用数组装乘客Bus
      Bus : 上车,下车 , 卖票等行为
            车上有Teacher,Student ,计算总票价

 


1、               本对象  子类对象  其他函数
 private属性      可见    不可见     不可见
 protected属性     可见     可见      不可见
 public属性         可见     可见       可见
 
                      public extends       protected extends       private extends
 父类的private属性       不能访问               不能访问                  不能访问
 父类的protected属性  变成protected                 不变             变成private,子类可以访问,子类的子类不能访问
 父类的public属性          不变               变成protected          变成private,子类可以访问,子类的子类不能访问

2、 构造函数有很多种,因为没有指定构造函数,就会默认使用无参的构造函数。如果父类没有无参的构造函数,那么就会出现编译错误。
    可以使用这种形式Teacher(char* name, int age, double salary):Person(name,age){......},指定使用父类的有参构造函数。
 
3、多态的特征:
        父类的指针可以指向子类的对象
        通过父类指针只能调用父类中声明的方法
        通过指针调用函数的时候,若函数是个虚函数,表现出来的行为是对象自身的行为
           
4、产生多态:(1)指针
             (2)引用
   父类的引用可以引用一个子类对象
   通过父类引用只能调用父类函数
   调用一个父类被覆盖了的,虚函数,能调用子类的函数
  
5、一个子类继承一个父类  --- 单继承
   一个子类继承多个父类  --- 多继承   
  
   class SpiderMan : public Spider , public Person{....}                             
  
6、菱形继承,解决重复元素的冲突
   让两个父类同时虚继承一个超类,把多继承中的重复元素放在超父类中
   当有多个子类同时虚继承一个父类的时候,只有一个子类真正的构造父类
   class Spider : vertual public Animal{.....};
   class Person : vertual public Animal{.....};
   class SpiderMan :public Person , public Spider{....};
  
   多继承尽量不要使用三层以上
  
7、抽象类
   只有函数声明,没有函数实现
   纯虚函数:没有实现的函数  virtual void writeLog(char*)=0;
             若不写" =0 ",则系统会认为是函数声明,会试图去别的" .cc "文件中去找函数实现
   含有纯虚函数的类称为抽象类,是抽象数据类型,不能创建对象
   抽象类型就是为了被别人继承的,子类覆盖纯虚函数,提供函数实现
   通过父类规范子类的用法
  
   如果子类没有完全实现抽象父类的所有纯虚函数,则认为子类还是一个抽象数据类型
  
   用到virtual的地方:
   (1)继承
   (2)多继承
   (3)纯虚函数
  
   抽象类的规范很重要,在工程中,对于项目的并行开发很重要
   而且对于项目中的改动,能方便的应付
   用指针指向相应的子类对象,方便的调用子类的函数
     
8、友员
   针对类来说,自身的私有变量,不能被别的类访问,但是,如果授权给一个类为自己的友员,就可以访问他的私有属性
   可以作为友员的东西:另一个类,一个全局函数。
  
   实现友员的授权:
      class Girl;
      class  Person{
           ........
           friend class Girl;  //友员类的声明-->授权给Girl类,成为自己的友员,可以访问自己的私有变量了
      }
  ----------------------------------------------------------------------------------------------------------------
      class Girl;
      class  Person{
           ........
           friend void fn();  //友员函数的声明-->授权给fn函数,成为自己的友员,可以访问自己的私有变量了
      }
     
    友员不是类的一部分
    若不是互为友员,则不能访问友员类的私有变量
   
    友员的使用:
           Bus把售票员作为自己的友员,访问自己的私有变量,即装载乘客的数组
          
    友员在项目中的使用
   
9、静态数据
   在类中定义一个静态数据  (实际上就是一种全局变量)
   (1)不依赖于对象,在对象不存在之前就已经存在了
   (2)所有对象共享   
  
   与全局变量的区别:
   (1)使用的类中的静态变量,必须通过类名使用
   (2)而且受访问控制符号的限制
   (3)静态变量在类中声明的时候不能赋值,要在类外初始化
        class A{
        public :
            static int a;
        };
        int A::a = 100;  //此时才分配空间

        int main(){
             cout << A::a <<endl;    //静态变量的用法,不依赖对象,直接使用
        }
       
    与成员变量的区别
    (1)成员变量依赖与对象,类对象不存在,成员变量也不存在
         静态变量不依赖于对象,只要有类声明,就存在
    (2)所有对象共享一份静态变量
   
10、静态函数
   在函数前加static
   不依赖于对象,没有对象依然可以通过类名调用静态函数
   A::fn();
  
   在类声明的时候,静态函数和静态变量就存在了
   静态函数只能使用静态变量,不能使用成员变量
  
11、拷贝构造函数
   #include <iostream>
   using namespace std;

   class Person{
   public:
      Person(){ cout<<"Person()"<<endl; }
      ~Person(){ cout<<"~Person() "<<endl;}
      void speak(){ cout<<"hello"<<endl; }
   };

   void fn( Person p ){  //这里的传递时值传递,形参是值传递,这里的形参的创建是使用拷贝构造函数
      p.speak();
   }

   int main(){
      Person p ;
      fn( p ) ;
      return 0 ;
   }  
   输出结果:
       Person()
       hello
       ~Person()
       ~Person()         //2次析构,因为其中调用了的系统提供的拷贝构造函数,构造出一个新对象

   拷贝构造函数,以一个已存在的对象为模版创建一个新对象
  声明方法: Person(const Person & p){...}  //即节省空间,又保证模版不会被修改
  
   默认拷贝构造函数,执行的就是简单的内存拷贝 --- 浅拷贝

   (1)浅拷贝
        只拷贝地址,而不是对指针指向空间的拷贝,会造成2个指针指向同一个空间
   (2)深拷贝
        为指针创建新空间,拷贝指针指向空间的内容
  
   调用拷贝构造函数的时机:    
   (1)在值传递的时候
   (2)A a;        //默认构造函数
        A a1 = a;   //在声明的时候,用一个对象赋值,使用的是拷贝构造函数
   (3)A a1;
        A a2(a1);   //显示的调用拷贝构造函数,传的参数是个对象
       
    什么时候需要自己写拷贝构造函数?
         使用了动态内存,就会面临浅拷贝的现象,需要自己写拷贝构造函数

12、运算符重载
     a1 = a2;
     系统调用了函数 operator=
     相当于执行了这样的操作:   a1.operator=(a2);      
    
     Student& operator= (const Student &a);
     函数返回左值,返回值为引用
     函数返回右值,返回值为本身
    
     /*
     *student a1;
     *student a2;
     *a1=a2;  <=>  a1.operator=(a2);
     */
     Student& operator= (const Student &a){
          age = a.age;
          id = a.id;
          strcpy(name , a.name);   //不用new出一块空间,因为在声明a1和a2的时候,两个指针都指向一块自己的空间,
                                     把指针指向的变量拷贝过去,即完成赋值
          return *this;
     }
     当在堆中申请空间,则覆盖赋值运算符(" = ")
    
作业:银行系统的账户类
      把name , password 改用指针保存
      (1)析构函数
      (2)拷贝构造
      (3)重载赋值运算符    

 

                     
1、拷贝构造函数和运算符重载
  (1)当类的成员变量中出现指针类型的时候,需要动态申请空间,这样就需要解决浅拷贝的问题
       在声明对象的同时用另一个对象为其赋值,会调用拷贝构造函数。
       系统提供的默认拷贝构造函数,是浅拷贝,我们可以自己写一个拷贝构造函数,把指针指向的变量也拷贝过去
  (2)类中的成员变量出现指针类型,当两个对象都创建出来了以后,相互赋值的时候,就需要重载赋值运算符号
       手工为指针指向的变量赋值
                                   
2、其他的运算符号重载
   对于对象之间的加减操作,系统是不允许的,但通过自己的运算符重载,按照自己的规则,实现对象之间的运算操作。
   Integer operator+(const Integer& i){
          int t = *p + *(i.p);
          Integer temp(t);
          return temp;
   }                 
  
   (1)自增运算符
      前++是左值,返回引用
      后++是右值,返回临时值
     
      "++"运算符的优先级比"+"高
     
      Integer& operator++(){}
      Integer operator++(int i){}
      int i是用于区别前++和后++的,是没有实际意义的参数,称为哑元,必须是int类型
     
      前++和后++的操作,主要区别就是返回的值不同,内部都是把变量加1。
      前++,(++i)先加后用,返回加1之后的变量值,可以把变量直接加1,就返回,所有可以直接返回引用
      后++,(i++)先用后加,返回加1之前的变量值,就是要返回原来的旧值,
             这样需要在重载运算符的函数内部创建一个对象保存旧值,再进行加1运算,返回这个旧值本身。
            
   (2)重载"="
       实现用一个int类型给一个Integer对象赋值
       Integer& operator=(int i){   //赋值运算,把对象内的int值改变,返回本身即可,所以返回值是引用
                *p = i;             //手工将int类型的值赋到对象内
                return *this;
       } 
      
   (3)运算符重载
        不仅可以用类的成员函数实现,也可以用普通函数实现
        用成员函数实现,参数只有一个,运算符左边的是自身,右边的是参数  a1.operator=(a2);
        用普通函数实现,参数需要两个,第一个参数是运算符左边的值,第二个参数是运算符右边的值,作为友员函数重载                                
        operator(a1,a2);
     
   (4)推荐原则
        所有一元运算符  ---  成员重载    "=","[]"只能成员重载
        二元运算符 --- 友员重载
       
        Integer对象类型与int类型相加的时候,
        实现 5+i 而且也能 i+5
        可以用友员重载2次
        friend Integer operator+(const Integer& i , int a);
        friend Integer operator+(int a , const Integer& i );  //在类中友员函数的声明
       
    (5)强制类型转换运算符
          operator int(){......}  //强转成int类型的声明     
         
3、流操作符的重载
  (1)输出流操作符只能使用友员方式重载
       friend ostream& operator<< (ostream & o,Integer & i);  //声明友员函数
       ostream& operator<< (ostream & o ,Integer & i){        //实现
            o << *(i.p) ;           //输出对象内的*p,即指针指向的变量
            return o;
       }         
       
       cout << i ;   <=>   operator(cout,i);
      
   (2)输入运算符也能重载,实现对象的读入,只能使用友员函数重载
       friend istream& operator>> (istream & in,Integer & i);  //声明友员函数
       istream& operator>> (istream & in ,Integer & i){        //实现
            in >> *(i.p) ;          //把读入的数据放到*p指向的变量中
            return in;
       }           
      
   (3)为什么只能用友员重载?
        因为cin cout 的位置是固定的,  cin >> i ; cout << i ;
        这样不能利用对象本身调用重载的流操作符  i.operator(cout) ->这样是不正确的
        只能使用友员重载,把对象和cout都当做参数传进去,这样就能操作了
       
练习:账号类,直接输出账号              
      Account a1;
      cout << a1 << endl;
     
       friend ostream& operator<< (ostream & o,Account & a);
       ostream& operator<< (ostream & o ,Account & a){      
            o << "Name     : " << a.name <<endl;
            o << "password : " << a.password << endl;          
            o << "id       : " << a.id << endl;
            o << "balance  : " << a.balance  ;
           
            return o;
       }      
     
      friend istream& operator>> (istream & in,Account & a);
       istream& operator>> (istream & in ,Account & a){        
            cout << "enter your name >";
            in >> a.name;       
            cout << "enter your password >";
            in >> a.password;
            cout << "enter your id >";
            in >> a.id;
            cout << "enter your balance >";
            in >> a.balance;
            return in;
       }     
      
作业 :
     写一个类叫“rmb”
     RMB{
        int * yuan;
        int * jiao;
        int * fen;
       
        RMB();
        RMB(int y , int j ,int f);
        RMB(const RMB & r);
        ~RMB();
        operator=
        friend ostream& operator<< (ostream & o,RMB &r);
        friend istream& operator>> (istream & in,RMB &r);
        人民币之间的运算,及与int的乘法
        operator double(){} //强转运算
        operator float(){}
     }     

 

 1、数据  内存中  变量
         磁盘上  文件
        
2、把数据从其他的设备搬到内存中 --- 输入 --- 读
   把内存中的数据放到其他设备中 --- 输出 --- 写
  
3、流
   物质的定向移动,输入输出流中是数据的定向移动
   输入流的源头 : 文件       目的地:内存
   输出流的源头 : 内存       目的地:文件
  
4、标准输出设备 --- 显示器
   标准输入设备 --- 键盘
  
    键盘 --- 内存 --- 显示器
          |        |
      输入操作   输出操作           
     
   输入输出流 : 内存与磁盘之间,内存与标准输入输出设备之间的     
  
5、cout  源 :变量       目的地 :显示器 
   cin        键盘                内存中某一变量
  
6、标准输入流 cin   istream的一个对象
   标准输出流 cout  ostream的一个对象
   标准错误流 cerr 目的地都是屏幕,用cout替代
  
7、cin
    是一个带有缓冲的标准的输入对象,默认输入设备是键盘
  (1) >>  : 自动校验数据类型  
              遇到回车才会开始读数据,遇到空格就结束,只能读一个单词
              流是数据的定向移动,被读走的数据就消失,没有读走的数据会一直留在流中,直到流的消失,数据也跟着消失
              流中有数据,就会阻塞,等待读取  ---  所有输入方法的特性
              为什么 ">>"可以连续使用? 因为返回就是istream对象本身的引用
              注意 : ">>" 不读回车,不读空格
  (2)get(): 每次 读一个字符。返回一个整数,实际上是读到字符的ASCII码
                把回车,空格都当作普通字符读出来
  (3)get(char&):把读到的内容存到参数中               
                  cin.get(arr[0]).get(arr[1]);  //get(char&)返回cin本身,可以连续使用
 (4)getline(str,256) : 读取一行,包括空格
          对于回车,只读走,不保存
          会读取数组长度减1个字符,最后一个放'\0'
                   输入数据超过给定的空间 (1)截断数据,剩下的数据还在流里
                                          (2)设置一个错误标记,调用cin.clear(),清除错误,继续工作
                     #include <iostream>
                     using namespace std;

                     int main(){
                          int age;
                          char name[20] ;
                          cout << "enter your age >";
                          cin >> age;
                          cin.get();            //读取流中残余的回车,以便getline能正常工作
                          // cin.ignore(20,'\n');  //忽略20个字符或者碰到回车,从流中清除
                          cout << "enter your name >";
                          cin.getline(name,20);
                          cout << "your age is :" << age << endl;
                          cout << "your name is :" << name << endl;
                     }
                                   
  (5)read(char*,int)   char*是存结果的地址,int是读的长度,并且不能比前面的数组的空间大
                         读满为止 ,特殊字符也当做普通字符处理
                         超出的部分仍然存在流里面
                         只要数据没读满,一直阻塞
                         不会自动补'\0' --- 传参数的时候,数组长度传减1的长度,补齐'\0'以免乱码
                        
   所有输入流的共同特征:只要没数据就阻塞
                         读不完的就留在流里                                        
                        
  (6)cin.ignore(255,'\n')  忽略255个字符或者遇到'\n',如果在前255个字符中出现'\n',则就忽略到'\n'之前的字符                        
  (7)peek()  查看流里的第一个字符是什么
               只察看,不读走
  (8)putback() 向流中插入字符,前提必须有空位的时候
                 必须与get()一起使用,用get()取出一个字符,才能用putback()插入一个字符
  (9)cin.fail()判断是否出现错误标志,一切正常返回false
 
  当用cin读取的数据类型出现错误的时候,这是一种不可恢复的错误,用cin.clear()是不能清除错误标记的
  在键盘上读取,用getline()可以确保输入流的正常形成,读取后采取强制转换类型得到自己需要的数据
 
8、ifstream
   (1)需要#include <fstream>头文件
      ifstream ifs("test.txt");  //创建一个ifstream的对象,打开文件,给构造函数传如一个参数,就是文要打开的文件名 
      //文件的在当前目录下找,也可以用相对路径或绝对路径找文件
      在打开文件之后,立即判断打开文件是否成功
      if( ifs.fail() ){
         cout << "Can't open test " <<endl;
         return 0;
      }
     
      if( ! ifs ){    //也可以这样判断文件打开是否出错
         cout << "Can't open test " <<endl;
         return 0;
      }
     
   (2)在文件结束的时候都会有"EOF"标志,作为文件结束的标志符
      可以用判断是否读到"EOF",来判断时候读到文件尾了
      if(ifs.eof()){
         break;
      }
     
   (3)对于一个进程可打开文件的数量是有数的,所以文件属于资源
      所以在使用完毕以后,要关闭文件输入流     
     
   练习:(1)把/etc/passwd 文件打印出来
         (2)把文件中不以"#"开头的内容读出来
            peek(),ignore()
           
9、输出操作cout
   (1) << 操作 被输出到屏幕上的东西,只所以能输出,都是以字符串类型输出
                 也就是说这个操作有自动类型转换的功能
   (2)put()   返回cout引用,可以连续调用           
   (3)write(buf,len)  按指定长度写到屏幕上 buf是char*类型
   (4)cout.width(10); 打印的内容总共占10个字符,并靠右对齐
                        只对其后边的一个cout生效
   (5)cout.fill('#'); 用'#'补齐空位
                        主要调用一次,对以后的都生效      
   (6)setf()操作,控制格式,教材188页                                        
  
   (7)特殊字符
        '\r'  表示回退一个格,再输出
        '\n'  回车
        \\    输出一个'\',因为'\'会被认为是转义字符
        '\t'  一个tab键
       
   (8)输出控制符
        oct    按八进制输出  “0”
        dec    使进制输出
        hex    按十六进制输出 “0x”
        flush  清空缓冲区 带缓冲区是因为和外部设备交涉,这样能减少向屏幕输出的次数,提高效率
               回车、程序结束和flush都是刷新缓存的命令
               cout << "a" <<flush ;
               
10、ofstream
    打开文件,把数据写进文件,关闭文件               
    ofstream ofs("ofstream.txt");   //打开文件,若文件不存在,创建,存在,打开
    if(ofs.fail()){                 //写文件失败,一般是权限问题
         cout << "open file error "<<endl;
         return 0;
    }
   
    在iostream头文件中cin cout对象已经被声明,可以直接使用,因为标准输入输出设备是唯一的,系统声明
    但是fstream的对象要程序员来声明,因为对文件的输入输出是不唯一的
   
     ofstream ofs("ofstream.txt" , ios::app);  //以追加的形式向文件中写
     ios::trunc 默认方式  把文件内容清空,写新的文件
     ios::nocreate  不创建新文件
     ios::noreplace  不改写,要创建新文件
     组合多个 ofstream ofs("ofstream.txt" , ios::app | ios::in | ios::binary);
    
11、读写二进制文件
    (1)ios::binary
    (2)read/write 方法读写二进制文件,因为这两个方法只需要起始位置和大小就可以工作
   
     
作业:(1)int readInt(){}
           double readDouble(){}
           强转函数,要求容错能力强,要求有可靠的手段通知用户是否出错                                
      (2)根据用户输入的用户名,打印他的用户id,home,shell,如果用户不存在,通知用户
           strtok(),strcmp()          
     

 

 

 

posted @ 2013-06-25 12:41  夜雨阑珊  阅读(229)  评论(0编辑  收藏  举报