C++核心编程(二)

一、内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的

  • 全局区:存放全局变量和静态变量以及常量

  • 栈区:由编译器自动分配释放,存放函数的参数值、局部变量等

  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统收回

1.程序运行前

在程序编译后,生成了.exe可执行程序,未执行该程序前分为两个区域

代码区:

  • 存放CPU执行的机器指令

  • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

  • 代码区是只读的,使其只读的原因是防止程序意外地修改了他的指令

全局区:

  • 全局变量、静态变量和常量区(字符串常量和其他常量)存放在此

    • 局部变量:写在函数体内的变量

    • 全局变量:不写在函数体内的变量

    • 静态变量:在普通变量前面加static

      #include<iostream>
      using namespace std;
      ​
      // 全局变量
      int a=10;
      ​
      // const修改的全局变量——const修饰的常量
      const int a2=10;
      ​
      int main(){
          
          // 局部变量
          int b=10;
          
          // 静态变量
          static int c=10;
          
          // 字符串常量
          cout<<"好好学习"<<endl;
          
          // const修改的局部变量——const修饰的常量
          const int a2=10;
      ​
          return 0;
      }
  • 该区域的数据在程序结束后由操作系统释放

2.程序运行后

栈区:

  • 由编译器自动分配释放,存放函数的参数值、局部变量等

  • 注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区:

  • 由程序员分配和释放,若程序员不释放,程序结束时由操作系统收回

  • 在C++中主要利用new在堆区开辟内存

    #include<iostream>
    using namespace std;
    ​
    int * func(){
        int * p=new int(10);
        return p;
    }
    ​
    int main(){
        
        int * p=func();
        cout<<*p<<endl;
        
        return 0;
    }

3.new操作符

C++中利用new操作符在堆区开辟数据

堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete

语法:new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针

#include<iostream>
using namespace std;
​
int * func(){
    int * p=new int(10);
    return p;
}
​
int main(){
    
    int * p=func();
    cout<<*p<<endl;
    
    delete p;
    
    return 0;
}
数组
#include<iostream>
using namespace std;
​
void * func(){
    int * arr=new int[10];
    for(int i=0;i<10;i++){
        arr[i]=i+100;
    }
    for(int i=0;i<10;i++){
        cout<<arr[i]<<endl;
    }
    
    delete[] arr;
}
​
int main(){
    
    func();
    
    return 0;
}

二、引用

1.引用的基本使用

作用:给变量起别名

语法:数据类型 &别名 = 原名;

#include<iostream>
using namespace std;
​
int main(){
    
    int a=10;
    int &b = a;
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
    
    b=100;
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
    
    return 0;
}
​
结果:
10
10
100
100

2.引用的注意事项

  • 引用必须初始化

  • 引用在初始化后,不可以改变

3.引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

#include<iostream>
using namespace std;
​
// 1.值传递
void swap01(int a, int b){
    int temp=a;
    a=b;
    b=temp;
}
​
// 2.地址传递
void swap02(int *a, int *b){
    int temp=*a;
    *a=*b;
    *b=temp;
}
​
// 3.引用传递
void swap03(int &a, int &b){
    int temp=a;
    a=b;
    b=temp;
}
​
​
int main(){
    
    int a=10,b=3;
    swap01(a,b);
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
    
    swap02(&a,&b);
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
    
    swap03(a,b);
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
    
    return 0;
}
​
结果:
a=10
b=3
​
a=3
b=10
​
a=3
b=10

4.引用做函数的返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用

用法:函数调用作为左值

#include<iostream>
using namespace std;
​
int& test01(int a, int b){
    int a=10;
    return a;
}
​
int& test02(int a, int b){
    static int a=10;
    return a;
}
​
int main(){
    
    int &ref01 = test01();
    cout<<ref01<<endl;
    cout<<ref01<<endl; // 第二次结果错误,因为a的内存已经释放
    
    int &ref02 = test01();
    cout<<ref02<<endl;
    cout<<ref02<<endl;
    
    test01()=1000;
    cout<<ref02<<endl;
    cout<<ref02<<endl;
    
    return 0;
}
​
结果:
10
24197417987
​
10
10
​
1000
1000

5.引用的本质

引用的本质是在C++内部实现一个指针常量。

int * const ref = &a;

6.常量引用

作用:常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

#include<iostream>
using namespace std;
​
void show01(int &val){
    val=1000;
    cout<<val<<endl;
}
​
void show02(const int &val){
    cout<<val<<endl;
}
​
int main(){
    
    // 加上const之后,编译器将代码修改 int temp = 10; const int &ref=temp;
    const int &ref=10;
    
    int a=100;
    
    show02(a);
    
    show01(a);
    
    return 0;
}
​
结果:
100
1000

三、函数的提高

1.函数默认参数

在C++中,函数的形参列表中的形参是可以有默认值的

语法:返回值类型 函数名 (参数 = 默认值){}

#include<iostream>
using namespace std;
​
int func01(int a, int b=10, int c=30);
​
int func01(int a, int b, int c){
    return a+b+c;
}
​
int func(int a, int b=10, int c=30){
    return a+b+c;
}
​
int main(){
​
    cout<<func(10)<<endl;
    
    cout<<func(10,30)<<endl;
    
    return 0;
}
​
结果:
50
70

注意:

  • 如果某个位置已经有了默认参数,那么从这个位置往后,从左往右都必须有默认值

  • 如果函数声明有默认参数,函数实现就不能有默认参数(声明和实现是能有一个有默认参数)

2.函数占位参数

C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法:返回值类型 函数名 (数据类型){}

#include <iostream>
using namespace std;
​
void func01(int , int a=10){
    cout<<"hello"<<endl;
}
​
void func(int){
    cout<<"hello"<<endl;
}
​
int main(){
​
    func(10);
    func01(10);
    
    return 0;
}

3.函数重载

作用:函数名可以相同,提高复用性

满足函数重载的条件:

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同或者个数不同或者顺序不同

#include <iostream>
using namespace std;
​
void func(){
    cout<<"hello"<<endl;
}
​
​
void func(int a){
    cout<<"hello"<<endl;
}
​
void func(int a, int b){
    cout<<"hello"<<endl;
}
​
void func(int a, float b){
    cout<<"hello"<<endl;
}
​
void func(float a, int b){
    cout<<"hello"<<endl;
}
​
int main(){
​
    func();
    func(10);
    func(10,10);
    func(10,10);
    func(10.0f,10);
    
    return 0;
}

注意事项:

  • 引用作为重载条件

  • 函数重载碰到函数默认参数

#include <iostream>
using namespace std;
​
void func(int &a){
    cout<<"hello"<<endl;
}
​
void func(const int &a){
    cout<<"hello"<<endl;
}
​
void func2(int a){
    cout<<"hello"<<endl;
}
​
void func2(int a, int b = 10){
    cout<<"hello"<<endl;
}
​
int main(){
​
    int a=10;
    func(a); // 调用第一个
    func(10); // 调用第二个
    
    func2(10); // 出错,两个函数都可以调用(二义性)
    func2(10,10); // 调用第二个
    
    return 0;
}

四、类与对象

C++面向对象的三大特性:封装、继承、多态

1.封装

封装的意义

  • 将属性和行为作为一个整体,表现生活中的事物

    • 语法:class 类名{ 访问权限:属性 / 行为 };

      #include <iostream>
      using namespace std;
      ​
      const double PI=3.14;
      ​
      // 类
      class Circle{
          // 访问权限
          public:
              // 属性
              int r;
      ​
              // 行为
              double calculateZC(){
                  return 2*PI*r;
          }
      };
      ​
      int main(){
      ​
          double s=0.0;
      ​
          // 对象
          Circle cl;
          cl.r=10;
          s=cl.calculateZC();
          cout<<"圆的周长:"<<s<<endl;
          
          return 0;
      }
  • 将属性和行为加以权限控制

    • public——公共权限(成员类内可以访问,类外可以访问)

    • protected——保护权限(成员类内可以访问,类外不可以访问,儿子可以访问父亲中的保护内容)

    • private——私有权限(成员类内可以访问,类外不可以访问,儿子不可以访问父亲中的私有内容)

      • 优点:①将所有成员属性设置为私有,可以自己控制读写权限;②对于写权限,我们可以检测数据的有效性

    #include <iostream>
    using namespace std;
    #include <string>
    ​
    // 类
    class Person{
        // 访问权限
        public:
            // 属性
            string name;
            
         protected:
            int age;
            
         private:
            int card_Password;
        
        public void func(){
            name="zhangsan";
            car="xiaoquche";
            card_Password=123;
        }
    };
    ​
    int main(){
    ​
        Person p1;
        p1.name="lisi";
        p1.car="bengchi"; // 错误,不可以访问
        pr.car_Password=456; // 错误,不可以访问
        
        return 0;
    }
    #include <iostream>
    using namespace std;
    #include <string>
    ​
    // 类
    class Person{
         // 属性
         string m_name; // 可读可写
         int m_age; // 只读
         int m_age2=18; // 可读可写(0-100)
         int m_idol; // 只写
        
        public:
            void setName(string name){
                m_name=name;
            }
            string getName(){
                return m_name;
            }
        
            int getAge(){
                return m_age;
            }
            
            void setIdol(string idol){
                m_idol=idol;
            }
    ​
            string getIdol(string idol){
                return m_idol;
            }
            
            int setAge2(int age2){
                if(age2<0||age2>100){
                    cout<<"年龄输入有误!"<<endl;
                    return;
                }
               m_age2=age2;
            }
            
            int getAge2(){
                return age2;
            }
    };
    ​
    int main(){
    ​
        Person p1;
        p1.setName("lisi");
        cout<<p1.getName()<<endl;
        
        cout<<p1.getAge()<<endl;
        
        p1.setIdol("meimei");
        cout<<p1.getIdol()<<endl;
        
        p1.setAge2(110);
        cout<<p1.getAge2()<<endl; // 年龄输入有误!
        
        return 0;
    }

示例:

#include <iostream>
using namespace std;
​
const double PI=3.14;
​
// 类
class Cube{
    // 访问权限
    private:
        // 属性
        int L;
        int W;
        int H;
​
    public:
        void setL(int l){
            L=l;
        }
        int getL(){
            return L;
        }
​
        void setW(int w){
            W=w;
        }
        int getW(){
            return W;
        }
​
        void setH(int h){
            H=h;
        }
        int getH(){
            return H;
        }
​
        // 行为
        int calculateS(){
            return 2*(L*W+L*H+W*H);
        }
​
        int calculateV(){
            return L*W*H;
        }
​
        // 利用成员函数判断两个立方体是否相等
        bool isSame2(Cube &c){
            if(L==c.getL() && W==c.getW() && H==c.getH()){
                return true;
            }
            return false;
        }
​
};
​
// 利用全局函数判断两个立方体是否相等
bool isSame(Cube &c1, Cube &c2){
    if(c1.getL()==c2.getL() && c1.getW()==c2.getW() && c1.getH()==c2.getH()){
        return true;
    }
    return false;
}
​
int main(){
​
    int s=0,v=0;
    // 对象
    Cube c1;
    c1.setL(10);
    c1.setW(10);
    c1.setH(10);
    s=c1.calculateS();
    v=c1.calculateV();
    cout<<"圆的周长:"<<s<<endl;
    cout<<"圆的体积:"<<v<<endl;
​
    Cube c2;
    c2.setL(10);
    c2.setW(10);
    c2.setH(10);
    
    bool ret=isSame(c1,c2);
    cout<<"全局函数判断是否相等:"<<ret<<endl;
​
    ret=c1.isSame2(c2);
    cout<<"成员函数判断是否相等:"<<ret<<endl;
​
    return 0;
}

2.对象的初始化和清理

2.1构造函数和析构函数

C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作,对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器在自动调用,无须手动调用

    • 语法:类名(){}

      ① 构造函数,没有返回值也不写void

      ② 函数名称与类名相同

      ③ 构造函数可以有参数,因此可以发生重载

      ④ 程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次

  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

    • 语法:~类名(){}

      ① 析构函数,没有返回值也不写void

      ② 函数名称与类名相同,在名称前加上符号 ~

      ③ 构造函数不可以有参数,因此不可以发生重载

      ④ 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次

#include <iostream>
using namespace std;
​
class Person{
    public:
        Person(){
            cout<<"Person构造函数的调用"<<endl;
        }
​
        ~Person(){
            cout<<"Person析构函数的调用"<<endl;
        }
​
};
​
void test01(){
    Person p;
}
​
int main(){
    test01();
}
2.2构造函数的分类及调用

两种分类方式:

  • 按参数分类:有参构造和无参构造

  • 按类型分类:普通构造和拷贝构造

三种调用方式:

  • 括号法

  • 显示法

  • 隐式转化法

#include <iostream>
using namespace std;
​
class Person{
    int age;
​
    public:
        // 无参构造函数
        Person(){
            cout<<"Person无参构造函数的调用"<<endl;
        }
        //有参构造函数
        Person(int a){
            age=a;
            cout<<"Person有参构造函数的调用"<<endl;
        }
        // 拷贝构造函数
        Person(const Person &p){
            age=p.age;
            cout<<"Person拷贝构造函数的调用"<<endl;
        }
​
        ~Person(){
            cout<<"Person析构函数的调用"<<endl;
        }
​
};
​
void test01(){
    // 括号法
    Person p1;
    Person p2(10);
    Person p3(p2);
    
    // 显示法
    Person p21;
    Person p22=Person(10);
    Person p23=Person(p22);
​
    // 隐式转化法
    Person p32=10;
    Person p33=p32;
}
​
int main(){
    test01();
}
2.3拷贝构造函数调用时机
  • 使用一个已经创建完毕的对象来初始化一个新对象

  • 值传递的方式给函数参数传值

  • 以值方式返回局部对象

#include <iostream>
using namespace std;
​
class Person{
    int age;
​
    public:
        // 无参构造函数
        Person(){
            cout<<"Person无参构造函数的调用"<<endl;
        }
        //有参构造函数
        Person(int a){
            age=a;
            cout<<"Person有参构造函数的调用"<<endl;
        }
        // 拷贝构造函数
        Person(const Person &p){
            age=p.age;
            cout<<"Person拷贝构造函数的调用"<<endl;
        }
​
        ~Person(){
            cout<<"Person析构函数的调用"<<endl;
        }
​
};
​
void doWork(Person p){
    
}
​
Person doWork2(){
    Person p1;
    return p1;
}
​
void test01(){
    // 括号法
    Person p1(10);
    Person p2(p1);
}
​
void test02(){
    Person p;
    doWork(p)
}
​
void test03(){
    Person p=doWork2();
}
​
int main(){
    test01();
    test02();
    test03();
}
2.4构造函数调用规则

默认情况下,C++编译器至少给一个类增加3个函数

  • 默认构造函数(无参,函数体为空)

  • 默认析构函数(无参,函数体为空)

  • 默认拷贝构造函数,对属性进行值拷贝

规则

  • 如果用户定义有参构造函数,C++不会提供默认无参构造,但会提供默认拷贝构造

  • 如果用户定义拷贝构造函数,C++不会再提供其他构造函数

2.5深拷贝与浅拷贝
  • 浅拷贝:简单的赋值拷贝操作

    浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。

  • 深拷贝:在堆区重新申请空间,进行拷贝操作

    深拷贝是指 源对象与拷贝对象互相独立 ,其中任何一个对象的改动都不会对另外一个对象造成影响。

#include <iostream>
using namespace std;
​
class Person{
    int age;
    int * height;
    
    public:
        // 无参构造函数
        Person(){
            cout<<"Person无参构造函数的调用"<<endl;
        }
        //有参构造函数
        Person(int a, int h){
            age=a;
            height = new int(h);
            cout<<"Person有参构造函数的调用"<<endl;
        }
​
        ~Person(){
        
            // 将堆区开辟的数据做释放操作
            if(height != NULL){
                delete height;
                height = NULL;
            }
            cout<<"Person析构函数的调用"<<endl;
        }
​
};
​
void test01(){
    Person p1(10,160);
    Person p2(p1);
}
​
int main(){
    test01();
}
​
错误:浅拷贝带来的问题就是堆区内存重复释放
修改后:
#include <iostream>
using namespace std;
​
class Person{
    int age;
    int * height;
    
    public:
        // 无参构造函数
        Person(){
            cout<<"Person无参构造函数的调用"<<endl;
        }
        //有参构造函数
        Person(int a, int h){
            age=a;
            height = new int(h);
            cout<<"Person有参构造函数的调用"<<endl;
        }
        // 拷贝构造函数
        Person(const Person &p){
            age=p.age;
            height=p.height; //错
            height = new int(*p.height);
            cout<<"Person拷贝构造函数的调用"<<endl;
        }
​
        ~Person(){
        
            // 将堆区开辟的数据做释放操作
            if(height != NULL){
                delete height;
                height = NULL;
            }
            cout<<"Person析构函数的调用"<<endl;
        }
​
};
​
void test01(){
    Person p1(10,160);
    Person p2(p1);
}
​
int main(){
    test01();
}
2.6初始化列表

作用:C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)...{}

#include <iostream>
using namespace std;
​
class Person{
    public:
         
        int a;
        int b;
        int c;
​
        // Person():a(10),b(10),c(10){
        //
        // }
        
        Person(int A, int B, int C):a(A),b(B),c(C){
​
        }
};
​
void test01(){
    
    //Person p;
    Person P(10.20,30)
    cout<<p.a<<endl;
    cout<<p.b<<endl;
    cout<<p.c<<endl;
}
​
int main(){
    test01();
}
2.7类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

当其它类对象作为本类成员,构造时先构造类对象,再构造自身;析构相反。

#include <iostream>
#include <string>
using namespace std;
​
class Phone{
    public:
        string pName;
​
        Phone(string pname){
            pName=pname;
            cout<<"phone构造函数调用"<<endl;
        }
        
        ~Phone(){
            cout<<"phone析构函数"<<endl;
        }
};
​
class Person{
    public:
         
        string Name;
        Phone phone;
​
        Person(string name, string pname):Name(name),phone(pname){
            cout<<"person构造函数调用"<<endl;
        }
        
        ~Person(){
            cout<<"person析构函数"<<endl;
        }
};
​
void test01(){
    
    Person p("张三","iPhone");
    cout<<p.Name<<endl;
    cout<<p.phone.pName<<endl;
}
​
int main(){
    test01();
}
​
结果:
phone构造函数调用
person构造函数调用
张三
iPhone
person析构函数
phone析构函数
2.8静态成员

静态成员就是再成员变量和成员函数前加上关键字static,称为静态成员。

分类:

  • 静态成员变量

    • 所有对象共享同一份数据

    • 再编译阶段分配内存

    • 类内声明,类外初始化

      #include <iostream>
      #include <string>
      using namespace std;
      ​
      class Person{
          public:
               
              static int A;
            
      };
      ​
      int Person::A=100;
      ​
      void test01(){
          
          Person p;
          cout<<p.A<<endl;
          
          Person p2;
          p2.A=200;
          cout<<p.A<<endl;
      }
      ​
      void test02(){
          // 通过对象访问
          Person p;
          cout<<p.A<<endl;
          
          // 通过类名访问
          cout<<Person::A<<endl;
          
          
      }
      ​
      int main(){
          test01();
          test02();
      }
      ​
      结果:
      100
      200
      200
      200
  • 静态成员函数

    • 所有对象共享同一个函数

    • 静态成员函数只能访问静态成员变量

      #include <iostream>
      #include <string>
      using namespace std;
      ​
      class Person{
          public:
              static int A;
              int B;
          
              static void func(){
                  A=200;
                  // B=100; 错误
                  cout<<"调用"<<endl;
              }
            
      };
      ​
      int Person::A=100;
      ​
      void test01(){
          // 通过对象访问
          Person p;
          p.func();
          
          // 通过类名访问
          Person::func();
          
      }
      ​
      int main(){
          test01();
      }

3.C++对象模型和this指针

3.1成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

#include <iostream>
#include <string>
using namespace std;
​
class Person{
   
};
​
class Person02{
    public:
        int A;
        static int B;
    
        void func(){}
        statci void func2(){}
      
};
int Person02::B=0;
​
void test01(){
    Person p;
    // 空对象占用内存空间为1
    // C++ 编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
    // 每个空对象也应该有一个独一无二的内存地址
    cout<<sizeof(p)<<endl;
}
​
void test02(){
    Person02 p2;
    cout<<sizeof(p2)<<endl;
}
​
int main(){
    test01();
    test02();
}
​
结果:
1
4
3.2this指针概念

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码,那么如何区分那个对象调用自己?——this指针就是指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分

  • 在类的非静态成员函数中返回对象本身,可使用return *this

#include <iostream>
using namespace std;
​
class Person{
    public:
        int age;
        
        Person(int age){
            this->age=age;
        }
        
        // 
        Person& PersonAddAge(Person &p){
            this->age+=p.age;
            // this指向p2的指针,而*this指向的就是p2这个对象本体
            return *this;
        }
      
};
​
void test01(){
    Person p1(18);
    cout<<p1.age<<endl;
    Person p2(10);
    // 链式编程思想
    p2.PersonAddAge(p1).PersonAddAge(p1);
    cout<<p2.age<<endl;
​
}
​
int main(){
    test01();
}
​
结果:
18
46
3.3空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到this指针,需要考虑代码的健壮性

#include <iostream>
using namespace std;
​
class Person{
    public:
        int age;
        
        void showClassName(){
            cout<<"就发发了"<<endl;
        }
        
        void showPersonAge(){
            if(this==NULL){
                return;
            }
            // 报错原因是因为传入的指针是NULL
            cout<<"age="<<this->age<<endl;
        }
      
};
​
void test01(){
    Person *p = NULL;
    p->showClassName();
    
    p->showPersonAge();
​
}
​
int main(){
    test01();
}
3.4const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数

  • 常函数内不可以修改成员属性

  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象

  • 常对象只能调用常函数

#include <iostream>
using namespace std;
​
class Person{
    public:
        int age;
        muable int B;
        
        void showPerson()nconst{
            // this->age=100; 错误
            this->B=10
        }
        
        void showPersonAge(){
            if(this==NULL){
                return;
            }
            // 报错原因是因为传入的指针是NULL
            cout<<"age="<<this->age<<endl;
        }
      
};
​
void test01(){
    Person p;
    p.showPerson();
    
}
​
void test01(){
    const Person p;
    // p.age=100;
    p.B=100;
    p.showPerson();
    
}
​
int main(){
    test01();
    test02();
}

4.友元

目的:让一个函数或者类访问另一个类中私有成员

关键字:friend

实现方式:

  • 全局函数做友元

  • 类做友元

  • 成员函数做友元

    #include <iostream>
    #include <string>
    using namespace std;
    ​
    class Building{
        public:
            string sittingRoom;
        private:
            string bedRoom;
    ​
        public:
            Building(){
                sittingRoom="客厅";
                bedRoom="卧室";
            }
    ​
        friend void goodFriend(Building &building);
       
    };
    ​
    // 全局函数
    void goodFriend(Building &building){
    ​
        cout<<building.sittingRoom<<endl;
        cout<<building.bedRoom<<endl;
    ​
    }
    ​
    void test(){
        Building building;
        goodFriend(building);
    }
    ​
    int main(){
        test();
    }
    #include <iostream>
    #include <string>
    using namespace std;
    ​
    class Building{
        public:
            string sittingRoom;
        private:
            string bedRoom;
    ​
        public:
            Building();
       
        friend class goodFriend;
    };
    ​
    class goodFriend{
        public:
            Building * building;
    ​
        public:
            void visit();
        
        public:
            goodFriend();
    ​
    };
    ​
    // 类外写成员函数
    Building::Building(){
        sittingRoom="客厅";
        bedRoom="卧室";
    }
    ​
    goodFriend::goodFriend(){
        building = new Building;
    }
    ​
    void goodFriend::visit(){
    ​
        cout<<"访问"<<building->sittingRoom<<endl;
        cout<<"访问"<<building->bedRoom<<endl;
    ​
    }
    ​
    void test(){
        
        goodFriend gg;
        gg.visit();
    ​
    }
    ​
    int main(){
        test();
    }
    #include <iostream>
    #include <string>
    using namespace std;
    ​
    class Building;
    class goodFriend{
        public:
            Building * building;
    ​
        public:
            void visit(); // 可以访问Building中私有成员
            void visit2(); // 不可以访问Building中私有成员
        
        public:
            goodFriend();
    ​
    };
    ​
    class Building{
        public:
            string sittingRoom;
        private:
            string bedRoom;
    ​
        public:
            Building();
       
            friend void goodFriend::visit();
    };
    ​
    // 类外写成员函数
    Building::Building(){
        sittingRoom="客厅";
        bedRoom="卧室";
    }
    ​
    goodFriend::goodFriend(){
        building = new Building;
    }
    ​
    void goodFriend::visit(){
    ​
        cout<<"visit访问"<<building->sittingRoom<<endl;
        cout<<"visit访问"<<building->bedRoom<<endl;
    ​
    }
    ​
    void goodFriend::visit2(){
    ​
        cout<<"vist2访问"<<building->sittingRoom<<endl;
        //cout<<"visit2访问"<<building->bedRoom<<endl;
    ​
    }
    ​
    void test(){
        
        goodFriend gg;
        gg.visit();
        gg.visit2();
    ​
    }
    ​
    int main(){
        test();
    }

5.运算符重载

对已有的运算符重载进行定义,赋予其另一种功能,以适应不同的数据类型

5.1加号运算符重载

作用:实现两个自定义数据类型相加的运算

#include<iostream>
using namespace std;
​
class Person{
    public:
        int A;
        int B;
​
        // 1.成员函数重载
        // Person operator+(Person &p){
        //     Person temp;
        //     temp.A=this->A+p.A;
        //     temp.B=this->B+p.B;
        //     return temp;
        // }
    
};
​
// 全局函数重载
Person operator+(Person &p1, Person &p2){
    Person temp;
    temp.A=p1.A+p2.A;
    temp.B=p1.B+p2.B;
    return temp;
}
​
// 函数重载的版本
Person operator+(Person &p1, int num){
    Person temp;
    temp.A=p1.A+num;
    temp.B=p1.B+num;
    return temp;
}
​
void test(){
    Person p1;
    p1.A=10;
    p1.B=10;
    Person p2;
    p2.A=10;
    p2.B=10;
​
    Person p3=p1+p2;
    // 成员函数的本质调用
    // Person p3=p1.operator+(p2);
​
    // 全局函数的本质调用
    // Person p3=operator+(p1,p2);
    cout<<p3.A<<endl;
    cout<<p3.B<<endl;
    
    // 运算符重载,也可以发生函数重载
    Person p4=p1+10;
    cout<<p4.A<<endl;
    cout<<p4.B<<endl;
​
}
int main(){
    test();
    return 0;
}
5.2左移运算符重载

作用:可以输出自定义数据类型

方式一:
#include<iostream>
using namespace std;
​
class Person{
    
    private:
        int A;
        int B;
​
};
​
// 只能利用全局函数重载左移运算符
ostream & operator<<(ostream &cout,Person &p){
    cout<<p.A<<" "<<p.B;
    return cout;
}
​
void test(){
    Person p;
    p.A=10;
    p.B=10;
   
    cout<<p<<endl;
​
}
int main(){
    test();
    return 0;
}
​
方式二:
#include<iostream>
using namespace std;
​
class Person{
    
    friend ostream & operator<<(ostream &cout,Person &p);
    private:
        int A;
        int B;
​
    public:
        Person(int a,int b){
            A=a;
            B=b;
        }
};
​
// 只能利用全局函数重载左移运算符
ostream & operator<<(ostream &cout,Person &p){
    cout<<p.A<<" "<<p.B;
    return cout;
}
​
void test(){
    Person p(10,10);
​
    cout<<p<<endl;
​
}
int main(){
    test();
    return 0;
}
5.3递增运算符重载

作用:用过重载递增运算符,实现自己的整型数据

#include<iostream>
using namespace std;
​
class MyInteger{
​
    friend ostream& operator<<(ostream& cout, MyInteger myint);
    
    private:
        int Num;
​
    public:
        MyInteger(){
            Num=0;
        }
​
        // 重载前置++运算符
        MyInteger& operator++(){
            Num++;
            return *this;
        }
​
        // 重载后置++运算符
        MyInteger operator++(int){
            MyInteger temp = *this;
            Num++;
            return temp;
        }
​
};
​
// 全局函数重载<<运算符
ostream& operator<<(ostream& cout, MyInteger myint){
    cout<<myint.Num;
    return cout;
}
​
void test01(){
    MyInteger myint;
​
    cout<<++(++myint)<<endl;
    cout<<++myint<<endl;
​
}
​
void test02(){
    MyInteger myint;
​
    cout<<myint++<<endl;
    cout<<myint<<endl;
​
}
​
int main(){
​
    test01();
    test02();
​
    return 0;
}
5.4递减运算符重载
#include<iostream>
using namespace std;
​
class MyInteger{
​
    friend ostream& operator<<(ostream& cout, MyInteger myint);
    
    private:
        int Num;
​
    public:
        MyInteger(){
            Num=3;
        }
​
        // 重载前置--运算符
        MyInteger& operator--(){
            Num--;
            return *this;
        }
​
        // 重载后置++运算符
        MyInteger operator--(int){
            MyInteger temp = *this;
            Num--;
            return temp;
        }
​
};
​
// 全局函数重载<<运算符
ostream& operator<<(ostream& cout, MyInteger myint){
    cout<<myint.Num;
    return cout;
}
​
void test01(){
    MyInteger myint;
​
    cout<<--(--myint)<<endl;
    cout<<--myint<<endl;
​
}
​
void test02(){
    MyInteger myint;
​
    cout<<myint--<<endl;
    cout<<myint<<endl;
​
}
​
int main(){
​
    test01();
    test02();
​
    return 0;
}
5.5赋值运算符重载

C++编译器至少给一个类添加4个函数

  1. 默认构造函数

  2. 默认析构函数

  3. 默认拷贝构造函数

  4. 赋值运算符 operator=,对属性进行值拷贝

#include<iostream>
using namespace std;
​
class Person{
    public:
        
        int *Age;
​
        Person(int age){
            Age=new int(age);
        }
​
        ~Person(){
            if(Age!=NULL){
                delete Age;
                Age=NULL;
            }
        }
​
        // 重载赋值运算符
        Person& operator=(Person &p){
            // 浅拷贝
            // Age=p.Age;
​
            if(Age!=NULL){
                delete Age;
                Age=NULL;
            }
​
            Age=new int(*p.Age);
​
            return *this;
        }
​
};
​
void test01(){
    Person p1(18);
    cout<<*p1.Age<<endl;
    
    Person p2(20);
    Person p3(30);
​
    p3=p2=p1;
    cout<<*p2.Age<<endl;
    cout<<*p3.Age<<endl;
}
​
int main(){
    test01();
    return 0;
}
5.6关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

#include<iostream>
#include<string>
using namespace std;
​
class Person{
    public:
        
        string Name;
        int Age;
​
        Person(string name, int age){
            Name = name;
            Age = age;
        }
​
        // 重载==号
        bool operator==(Person &p){
            if(this->Name==p.Name&&this->Age==p.Age){
                return true;
            }
            return false;
        }
​
        // 重载==号
        bool operator!=(Person &p){
            if(this->Name==p.Name&&this->Age==p.Age){
                return false;
            }
            return true;
        }
​
};
​
void test01(){
    Person p1("Tom",18);
    
    Person p2("Tom",18);
    if(p1==p2){
        cout<<"相等"<<endl;
    }
    else{
        cout<<"不相等"<<endl;
    }
​
    if(p1!=p2){
        cout<<"不相等"<<endl;
    }
    else{
        cout<<"相等"<<endl;
    }
​
}
​
int main(){
    test01();
    return 0;
}
5.7函数调用运算符重载
  • 函数调用运算符()也可以重载

  • 由于重载后使用的方法非常像函数的调用,因此称为仿函数

  • 仿函数没有固定写法,非常灵活

#include<iostream>
#include<string>
using namespace std;
​
class MyPrint{
    public:
        void operator()(string test){
            cout<<test<<endl;
        }
};
​
void MyPrint02(string test){
    cout<<test<<endl;
}
​
void test01(){
    MyPrint myPrint;
    myPrint("hello world");
​
    MyPrint02("hello world");
}
​
class MyAdd{
    public:
        int operator()(int v1, int v2){
            return v1+v2;
        }
};
​
void test02(){
    MyAdd add;
    int ret = add(10,10);
    cout<<ret<<endl;
​
    // 匿名对象调用
    cout<<MyAdd()(100,100)<<endl;
}
​
int main(){
    test01();
    test02();
    return 0;
}

6继承

语法:class 子类(派生类) :继承方式 父类(基类)

#include<iostream>
#include<string>
using namespace std;
​
// 普通实现页面
// class Java{
//     public:
//         void header(){
//             cout<<"首页、公开课、登录、注册"<<endl;
//         }
//         void footer(){
//             cout<<"帮助中心、交流合作、站内地图"<<endl;
//         }
//         void left(){
//             cout<<"Java、Python、C++"<<endl;
//         }
//         void content(){
//             cout<<"java学习视频"<<endl;
//         }
​
// };
​
// class Python{
//     public:
//         void header(){
//             cout<<"首页、公开课、登录、注册"<<endl;
//         }
//         void footer(){
//             cout<<"帮助中心、交流合作、站内地图"<<endl;
//         }
//         void left(){
//             cout<<"Java、Python、C++"<<endl;
//         }
//         void content(){
//             cout<<"python学习视频"<<endl;
//         }
​
// };
​
// class Cpp{
//     public:
//         void header(){
//             cout<<"首页、公开课、登录、注册"<<endl;
//         }
//         void footer(){
//             cout<<"帮助中心、交流合作、站内地图"<<endl;
//         }
//         void left(){
//             cout<<"Java、Python、C++"<<endl;
//         }
//         void content(){
//             cout<<"C++学习视频"<<endl;
//         }
​
// };
​
class BasePage{
    public:
        void header(){
            cout<<"首页、公开课、登录、注册"<<endl;
        }
        void footer(){
            cout<<"帮助中心、交流合作、站内地图"<<endl;
        }
        void left(){
            cout<<"Java、Python、C++"<<endl;
        }
​
};
​
class Java : public BasePage{
    public:
        void content(){
            cout<<"java学习时评"<<endl;
        }
};
​
class Python : public BasePage{
    public:
        void content(){
            cout<<"python学习时评"<<endl;
        }
};
​
class Cpp : public BasePage{
    public:
        void content(){
            cout<<"Cpp学习时评"<<endl;
        }
};
​
void test01(){
    cout<<"java下载视频页面如下:"<<endl;
    Java java;
    java.header();
    java.footer();
    java.left();
    java.content();
    cout<<"_______________________________"<<endl;
​
    cout<<"python下载视频页面如下:"<<endl;
    Python python;
    python.header();
    python.footer();
    python.left();
    python.content();
    cout<<"_______________________________"<<endl;
​
    cout<<"C++下载视频页面如下:"<<endl;
    Cpp cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();
}
​
int main(){
    test01();
​
    return 0;
}

继承方式:

  • 公共继承

  • 保护继承

  • 私有继承

class A{
    public:
        int a;
    protected:
        int b;
    private:
        int c;
};
​
class B : public A{
    public:
        int a;
    protected:
        int b;
    private:
        int c; // 不可访问
};
​
class B : protected A{
    protected:
        int a;
        int b;
    private:
        int c; // 不可访问
};
​
​
class B : private A{
    private:
        int a;
        int b;
    private:
        int c; // 不可访问
};
6.1继承种的对象模型
class Base{
    public:
        int A;
    protected:
        int B;
    private:
        int C;
};
​
class Son : public Base{
    public:
        int D;
    
};
​
void test01(){
    cout<<sizeof(Son)<<endl;
}
​
int main(){
    test01;
    
    return 0;
}
​
结果:
16
父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性是被编译器给隐藏了,因此是访问不到,但是确实继承下去了
​
6.2继承中的构造和析构顺序

先构造父类,再构造子类;析构的顺序与构造的顺序相反。

class Base{
    public:
        Base(){
            cout<<"base构造函数"<<endl;
        }
        ~Base(){
            cout<<"base析构函数"<<endl;
        }
};
​
class Son : public Base{
    public:
        Son(){
            cout<<"son构造函数"<<endl;
        }
        ~Son(){
            cout<<"son析构函数"<<endl;
        }
    
};
​
void test01(){
    Son s;
}
​
int main(){
    test01;
    
    return 0;
}
​
结果:
base构造函数
son构造函数
son析构函数
base析构函数
6.3继承中同名成员处理方式
  • 访问子列同名成员:直接访问即可

  • 访问父类同名成员:需要加作用域

class Base{
    public:
        int A;
        
        Base(){
            A=100;
        }
        
        void func(){
            cout<<"Base成员函数"<<endl;
        }
};
​
class Son : public Base{
    public:
        int A;
        
        Base(){
            A=200;
        }
        
        void func(){
            cout<<"Son成员函数"<<endl;
        }
    
};
​
void test01(){
    Son s;
    cout<<s.A<<endl;
    cout<<s.Base::A<<endl;
    
    s.func();
    s.Base::func();
}
​
int main(){
    test01;
    
    return 0;
}
​
结果:
200
100
Son成员函数
Base成员函数
6.4继承同名静态成员处理方式
  • 访问子列同名成员:直接访问即可

  • 访问父类同名成员:需要加作用域

class Base{
    public:
        static int A;
        
        static void func(){
            cout<<"Base成员函数"<<endl;
        }
};
int Base :: A=100;
​
class Son : public Base{
    public:
        static int A;
        
        static void func(){
            cout<<"Son成员函数"<<endl;
        }
    
};
int Son :: A=200;
​
// 通过对象访问
void test01(){
    Son s;
    cout<<s.A<<endl;
    cout<<s.Base::A<<endl;
    
    s.func();
    s.Base::func();
}
​
// 通过类名访问
void test02(){
    cout<<Son::A<<endl;
    cout<<Son::Base::A<<endl;
    
    Son::func();
    Son::Base::func();
}
​
int main(){
    test01;
    test02;
    
    return 0;
}
​
结果:
200
100
Son成员函数
Base成员函数
200
100
Son成员函数
Base成员函数
6.5多继承语法

语法:class 子类:继承方式 父类1,继承方式 父类2

class A{
    public:
        int a;
        A(){
            a=100;
        }
};
​
class B{
    public:
        int b;
        B(){
            b=200;
        }
};
​
class C : public A, public B{
    public:
        int c;
        C(){
            c=300;
        }
    
};
​
void test(){
    C cc;
    cout<<sizeof(cc)<<endl;
​
}
​
int main(){
    test;
    return 0;
}
​
结果:
12
6.6菱形继承

虚继承:virtual

class Animal{
    public:
        int Age;
};
​
class Sheep : virtual public Animal{
​
};
​
class Tuo : virtual public Animal{
​
};
​
class SheepTuo : publi Sheep, public Tuo{
​
};
​
void test(){
    SheepTuo st;
    st.Sheep::Age=18;
    st.Tuo::Age=28;
    
    cout<<st.Sheep::Age<<endl;
    cout<<st.Sheep::Age<<endl;
    cout<<st.Age<<endl;
    
}
​
int main(){
    test();
    
    return 0;
}
​
结果:
28
28
28

7.多态

分类

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名

  • 动态多态:派生类和虚函数实现运行时多态

    动态多态满足条件:

    • 有继承关系

    • 子类重写父类的虚函数

    多态使用条件:

    • 父类指针或引用指向子类对象

区别

  • 静态多态的函数地址早绑定——编译阶段确定函数地址

  • 动态多态的函数地址晚绑定——运行阶段确定函数地址

优点

  • 代码组织结构清晰

  • 可读性强

  • 利于前期和后期的扩展以及维护

#include<iostream>
#include<string>
using namespace std;
​
class Animal{
    public:
        virtual void speak(){
            cout<<"动物在说话"<<endl;
        }
​
};
​
class Cat : public Animal{
    public:
        void speak(){
            cout<<"小猫在说话"<<endl;
        }
};
​
class Dog : public Animal{
    public:
        void speak(){
            cout<<"小狗在说话"<<endl;
        }
};
​
// 地址早绑定
void doSpeak(Animal &animal){
    animal.speak();
}
​
void test01(){
    Cat cat;
    doSpeak(cat);
}
​
int main(){
    test01();
​
    return 0;
}

多态案例一——计算器类

开闭原则:对扩展进行开发,对修改进行关闭

#include<iostream>
#include<string>
using namespace std;
​
// 普通实现
class Calculator{
    public:
        int Num1;
        int Num2;
​
        int getResult(string oper){
            if(oper == "+"){
                return Num1+Num2;
            }
            if(oper == "-"){
                return Num1-Num2;
            }
            if(oper == "*"){
                return Num1*Num2;
            }
        }
};
​
void test01(){
    Calculator c;
    c.Num1=20;
    c.Num2=10;
    cout<<c.Num1<<"+"<<c.Num2<<"="<<c.getResult("+")<<endl;
    cout<<c.Num1<<"-"<<c.Num2<<"="<<c.getResult("-")<<endl;
    cout<<c.Num1<<"*"<<c.Num2<<"="<<c.getResult("*")<<endl;
}
​
// 利用多态实现
class AbstractCalculator{
    public:
        int Num1;
        int Num2;
​
        virtual int getResult(){
            return 0;
        }
};
​
class AddCalculator : public AbstractCalculator{
    public:
        int getResult(){
            return Num1+Num2;
        }
}; 
​
class SubCalculator : public AbstractCalculator{
    public:
        int getResult(){
            return Num1-Num2;
        }
}; 
​
class MuiCalculator : public AbstractCalculator{
    public:
        int getResult(){
            return Num1*Num2;
        }
}; 
​
void test02(){
    AbstractCalculator * abc = new AddCalculator;
    abc->Num1=10;
    abc->Num2=10;
    cout<<abc->Num1<<"+"<<abc->Num2<<"="<<abc->getResult()<<endl;
​
    delete abc;
}
​
int main(){
    test01();
​
    test02();
​
    return 0;
}
7.1纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数

语法:virtual 返回值类型 函数名 (参数列表)= 0

当类中有了纯虚函数,这个类也称为抽象类

抽象类的特点:

  • 无法实例化对象

  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

class Base{
    public:
        virtual void func() = 0;
};
​
class Son : public Base{
    public:
        virtual void func() {
            cout<<"调用"<<endl;
        };
}
​
void test(){
    Base * base = new Son;
    base->func();
}
​
int main(){
    
    test();
    return 0;
}
7.2虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。因此可以将父类中的析构函数改为虚析构或者纯虚析构。

共性:

  • k恶意解决父类指针释放子类对象

  • 都需要有具体的函数实现

区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:virtual ~类名() {}

纯虚析构语法:virtual ~类名() = 0

类名::~类名() {}

#include<iostream>
#include<string>
using namespace std;
​
class Animal{
    public:
    
        Animal(){
            cout<<"Animal构造函数调用"<<endl;
        }
        
        // 虚析构
        // virtual ~Animal(){
        //  cout<<"Animal析构函数调用"<<endl;
        // }
​
        // 纯虚析构
        virtual ~Animal()=0;
        
        virtual void speak() = 0;
​
};
​
Animal::~Animal(){
    cout<<"Animal纯虚析构函数调用"<<endl;
}
​
class Cat : public Animal{
    public:
        string * Name;
        
        void speak(){
            cout<<*Name<<"小猫在说话"<<endl;
        }
​
        Cat(string name){
            cout<<"Cat构造函数调用"<<endl;
            Name = new string(name);
        }
        
        ~Cat(){
            if(Name != NULL){
                cout<<"Cat析构函数调用"<<endl;
                delete Name;
                Name=NULL;
            }
        }
    
};
​
void test01(){
    Animal * animal = new Cat("Tom");
    animal->speak();
    delete animal;
​
}
​
int main(){
​
    test01();
    return 0;
}

8.文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放,所以我们通过文件可以将数据持久化。

C++中对文件操作需要包含头文件<fstream>

文件类型分为两种:

  1. 文本文件:文件以文件的ASCII码形式存储在计算机中

  2. 二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂他们

操作文件的三大类:

  1. ofstream:写操作

  2. ifstream:读操作

  3. fstream:读写操作

8.1文本文件

写文件

  1. 包含头文件

#include<fstream>

  1. 创建流对象

ofstream ofs;

  1. 打开文件

ofs.open("文件路径",打开方式);

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::ate初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

注意:文件打开方式可以配合使用,利用 | 操作符

  1. 写数据

ofs<<"写入的数据";

  1. 关闭文件

ofs.close();

#include<iostream>
using namespace std;
#include<fstream>
​
void test(){
    ofstream ofs;
​
    ofs.open("test01.txt",ios::out);
    ofs<<"姓名:小王"<<endl;
    ofs<<"性别:男"<<endl;
    ofs<<"爱好:王"<<endl;
​
    ofs.close();
​
}
​
int main(){
    test();
​
    return 0;
}

读文件

  1. 包含头文件

#include<fstream>

  1. 创建流对象

ifstream ifs;

  1. 打开文件并判断文件是否打开成功

ifs.open("文件路径",打开方式);

  1. 写数据

四种方式读取

  1. 关闭文件

ifs.close();

#include<iostream>
using namespace std;
#include<fstream>
​
void test(){
    ifstream ifs;
​
    ifs.open("test01.txt",ios::in);
​
    if(!ifs.is_open()){
        cout<<"文件打开失败"<<endl;
        return;
    }
​
    // 读数据
    // 方式一
    // char buf[1024]={0};
    // while(ifs>>buf){
    //  cout<<buf<<endl;
    // }
    // 方式二
    // char buf[1024]={0};
    // while(ifs.getline(buf,sizeof(buf))){
    //  cout<<buf<<endl;
    // }
    // 方式三
    // string buf;
    // while(getline(ifs,buf)){
    //  cout<<buf<<endl;
    // }
    // 方式四
    char c;
    while((c=ifs.get())!=EOF){
        cout<<c;
    }
​
    ifs.close();
​
}
​
int main(){
    test();
​
    return 0;
}
8.2二进制文件

打开方式:ios::binary

写文件

二进制方式写文件主要利用流对象调用成员函数write

函数原型:ostream& write(const char * buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

#include<iostream>
using namespace std;
#include<fstream>
​
class Person{
    public:
        char Name[64];
        int Age;
​
};
​
void test(){
    ofstream ofs("person.txt",ios::out | ios::binary);
​
    //ofs.open("person.txt",ios::out | ios::binary);
​
    Person p={"zhangsan",18};
    ofs.write((const char *)&p,sizeof(Person));
​
    ofs.close();
}
​
int main(){
    test();
    return 0;
}

读文件

二进制方式读文件主要利用流对象调用成员函数read

函数原型:istream& read(char * buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

#include<iostream>
using namespace std;
#include<fstream>
​
class Person{
    public:
        char Name[64];
        int Age;
​
};
​
void test(){
    ifstream ifs("person.txt",ios::in | ios::binary);
​
    //ofs.open("person.txt",ios::out | ios::binary);
​
    if(!ifs.is_open()){
        cout<<"文件打开失败"<<endl;
        return;
    }
​
    Person p;
    ifs.read((char *)&p,sizeof(Person));
    cout<<p.Name<<" "<<p.Age<<endl;
​
    ifs.close();
}
​
int main(){
    test();
    return 0;
}

实例:职工管理系统

职工管理系统.cpp

#include<iostream>
using namespace std;
#include "workerManager.h"
​
int main(){
    
    //实例化管理者对象
    WorkerManager wm;
​
    int choice = 0;
    while(true){
        wm.Show_Menu();
        cout<<"请输入您的选择:"<<endl;
        cin>>choice;
​
        switch(choice){
            case 0://退出系统
                wm.ExitSystem();
                break;
            case 1://增加职工
                wm.Add_Emp();
                break;
            case 2://显示职工
                wm.Show_Emp();
                break;
            case 3://删除职工
                wm.Del_Emp();
                break;
            case 4://修改职工
                wm.Mod_Emp();
                break;
            case 5://查找职工
                wm.Find_Emp();
                break;
            case 6://排序
                wm.Sort_Emp();
                break;
            case 7://清除
                wm.Clean_File();
                break;
            default:
                system("cls");
                break;
        }
    }
​
    return 0;
}

worker.h

#pragma once
#include<iostream>
#include<string>
using namespace std;
​
class Worker{
    public:
        int Id;
        string Name;
        int DeptId;
​
        //显示个人信息
        virtual void showInfo()=0;
​
        //获取岗位名称
        virtual string getDeptName()=0;
};

employee.h

#pragma once
#include<iostream>
#include "worker.h"
using namespace std;
​
class Employee : public Worker{
    public:
​
        //构造函数
        Employee(int id, string name, int dId);
​
        //显示个人信息
        virtual void showInfo();
​
        //获取岗位名称
        virtual string getDeptName();
};

employee.cpp

#include "employee.h"
​
// 构造函数
Employee::Employee(int id, string name, int dId){
    this->Id=id;
    this->Name=name;
    this->DeptId=dId;
}
​
// 显示个人信息
void Employee :: showInfo(){
    cout<<"职工编号:"<<this->Id
        <<"\t职工姓名:"<<this->Name
        <<"\t岗位:"<<this->getDeptName()
        <<"\t岗位职责:完成经理交给的任务"<<endl;
}
​
// 获取岗位名称
string Employee :: getDeptName(){
    return string("员工");
}

manager.h

#pragma once
#include<iostream>
#include "worker.h"
using namespace std;
​
class Manager : public Worker{
    public:
​
        Manager(int id, string name, int dId);
​
        virtual void showInfo();
​
        virtual string getDeptName();
};

manager.cpp

#include "manager.h"
​
Manager::Manager(int id, string name, int dId){
    this->Id=id;
    this->Name=name;
    this->DeptId=dId;
}
​
void Manager::showInfo(){
    cout<<"职工编号:"<<this->Id
        <<"\t职工姓名:"<<this->Name
        <<"\t岗位:"<<this->getDeptName()
        <<"\t岗位职责:完成老板交给的任务,并下发任务给员工"<<endl;
}
​
string Manager::getDeptName(){
    return string("经理");
}

boss.h

#pragma once
#include<iostream>
#include "worker.h"
using namespace std;
​
class Boss : public Worker{
    public:
​
        Boss(int id, string name, int dId);
​
        virtual void showInfo();
​
        virtual string getDeptName();
};

boss.cpp

#include "boss.h"
​
Boss::Boss(int id, string name, int dId){
    this->Id=id;
    this->Name=name;
    this->DeptId=dId;
}
​
void Boss::showInfo(){
    cout<<"职工编号:"<<this->Id
        <<"\t职工姓名:"<<this->Name
        <<"\t岗位:"<<this->getDeptName()
        <<"\t岗位职责:管理公司所有事务"<<endl;
}
​
string Boss::getDeptName(){
    return string("老板");
}

workerManager.h

#pragma once //防止头文件重复包含
#include<iostream>
using namespace std;
#include "worker.h"
#include "employee.h"
#include "manager.h"
#include "boss.h"
​
#include<fstream>
#define FILENAME "empFile.txt"
​
class WorkerManager{
    public:
        //记录文件中的人数个数
        int EmpNum;
​
        //员工数组的指针
        Worker ** EmpArray;
​
        //构造函数
        WorkerManager();
​
        //展示菜单
        void Show_Menu();
​
        //退出系统
        void ExitSystem();
​
        //添加职工
        void Add_Emp();
​
        //保存文件
        void save();
​
        //标志文件是否为空
        bool FileIsEmpty;
​
        //统计文件中的人数
        int get_EmpNum();
​
        //初始化员工
        void init_Emp();
​
        void Show_Emp();
​
        void Del_Emp();
​
        int IsExist(int id);
​
        void Mod_Emp();
​
        void Find_Emp();
​
        void Sort_Emp();
​
        void Clean_File();
​
        //析构函数
        ~WorkerManager();
};

workerManager.cpp

#include "workerManager.h"
​
WorkerManager::WorkerManager(){
    //1.文件不存在
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
​
    if(!ifs.is_open()){
        //cout<<"文件不存在"<<endl;
        //初始化属性
        //初始化记录人数
        this->EmpNum=0;
        //初始化数组指针
        this->EmpArray=NULL;
        //初始化文件是否为空
        this->FileIsEmpty=true;
        ifs.close();
        return;
    }
​
    //2.文件存在且数据为空
    char ch;
    ifs>>ch;
    if(ifs.eof()){
        //cout<<"文件为空"<<endl;
        this->EmpNum=0;
        this->EmpArray=NULL;
        this->FileIsEmpty=true;
        ifs.close();
        return;
    }
​
    //3.文件存在且保存职工数据
    int num = this->get_EmpNum();
    //cout<<"职工的人数为:"<<num<<endl;
    this->EmpNum=num;
​
    //开辟空间
    this->EmpArray=new Worker*[this->EmpNum];
    //将文件中的数据,存到数组中
    this->init_Emp();
​
    // for(int i=0;i<this->EmpNum;i++){
    //  cout<<"职工编号:"<<this->EmpArray[i]->Id
    //      <<"姓名:"<<this->EmpArray[i]->Name
    //      <<"部门编号:"<<this->EmpArray[i]->DeptId<<endl;
    // }
​
}
​
//展示菜单
void WorkerManager::Show_Menu(){
    // cout<<endl;
    cout<<"欢迎使用职工管理系统"<<endl;
    cout<<"0.退出管理系统"<<endl;
    cout<<"1.添加职工信息"<<endl;
    cout<<"2.显示职工信息"<<endl;
    cout<<"3.删除离职职工"<<endl;
    cout<<"4.修改职工信息"<<endl;
    cout<<"5.查找职工信息"<<endl;
    cout<<"6.按照编号排序"<<endl;
    cout<<"7.清空所有文档"<<endl;
    cout<<"***********************************"<<endl;
​
}
​
//退出系统
void WorkerManager::ExitSystem(){
    cout<<"欢迎下次使用"<<endl;
    system("pause");
    exit(0);
​
}
​
//添加职工
void WorkerManager::Add_Emp(){
    cout<<"请输入添加员工数量:"<<endl;
​
    int addNum=0;
    cin>>addNum;
​
    if(addNum>0){
        //计算空间大小
        int newSize=this->EmpNum+addNum;
​
        //开辟新空间
        Worker ** newSpace = new Worker*[newSize];
​
        //将原空间下内容存放到新空间下
        if(this->EmpArray!=NULL){
            for(int i=0;i<this->EmpNum;i++){
                newSpace[i]=this->EmpArray[i];
            }
        }
​
        //输入新数据
        for(int i=0;i<addNum;i++){
            int id;//职工编号
            string name;//姓名
            int dSelect;//岗位
​
            cout<<"请输入"<<i+1<<"个新职工编号:"<<endl;
            cin>>id;
            cout<<"请输入"<<i+1<<"个新职工姓名:"<<endl;
            cin>>name;
            cout<<"请选择该职工岗位:"<<endl;
            cout<<"1.普通职工"<<endl;
            cout<<"2.经理"<<endl;
            cout<<"3.老板"<<endl;
            cin>>dSelect;
​
            Worker * worker=NULL;
            switch(dSelect){
                case 1:
                    worker = new Employee(id,name,1);
                    break;
                case 2:
                    worker = new Manager(id,name,2);
                    break;
                case 3:
                    worker = new Boss(id,name,3);
                    break;
                default:
                    break;
            }
            newSpace[this->EmpNum+i]=worker;
​
        }
        //释放原有空间
        delete[] this->EmpArray;
        //更改新空间的指向
        this->EmpArray=newSpace;
        //更新新的职工人俗
        this->EmpNum=newSize;
        //更新职工不为空的标志
        this->FileIsEmpty=false;
        
        cout<<"成功添加"<<addNum<<"名新员工"<<endl;
​
        this->save();
    }
    else{
        cout<<"输入有误"<<endl;
    }
​
    system("pause");
    system("cls");
}
​
void WorkerManager::save(){
    ofstream ofs;
    ofs.open(FILENAME, ios::out);
​
    for(int i=0;i<this->EmpNum;i++){
        ofs<<this->EmpArray[i]->Id<<" "
           <<this->EmpArray[i]->Name<<" "
           <<this->EmpArray[i]->DeptId<<endl;
    }
​
    ofs.close();
}
​
int WorkerManager::get_EmpNum(){
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
​
    int id;
    string name;
    int dId;
​
    int num=0;
    while(ifs>>id && ifs>>name && ifs>>dId){
        num++;
    }
​
    // ifs.close();
    return num;
}
​
void WorkerManager::init_Emp(){
    ifstream ifs;
    ifs.open(FILENAME, ios::in);
​
    int id;
    string name;
    int dId;
​
    int index=0;
    while(ifs>>id && ifs>>name && ifs>>dId){
        Worker * worker =NULL;
​
        //int index=0;
        if(dId==1){
            worker=new Employee(id, name ,dId);
        }
        else if(dId==2){
            worker=new Manager(id, name ,dId);
        }
        else{
            worker=new Boss(id, name ,dId);
        }
        this->EmpArray[index]=worker;
        index++;
    }
    ifs.close();
}
​
void WorkerManager::Show_Emp(){
    if(this->FileIsEmpty){
        cout<<"文件不存在或者记录为空"<<endl;
    }
    else{
        for(int i=0;i<EmpNum;i++){
            this->EmpArray[i]->showInfo();
        }
    }
​
    system("pause");
    system("cls");
}
​
void WorkerManager::Del_Emp(){
    if(this->FileIsEmpty){
        cout<<"文件不存在或者为空"<<endl;
    }
    else{
        cout<<"请输入想要删除的员工编号:"<<endl;
        int id=0;
        cin>>id;
​
        int index=this->IsExist(id);
​
        if(index!=-1){
            for(int i=index;i<this->EmpNum-1;i++){
                this->EmpArray[i]=this->EmpArray[i+1];
            }
            this->EmpNum--;
            this->EmpArray[this->EmpNum] = NULL;
            this->save();
            cout<<"删除成功!"<<endl;
        }
        else{
            cout<<"删除失败,未找到该员工"<<endl;
        }
    }
    system("pause");
    system("cls");
}
​
int WorkerManager::IsExist(int id){
    int index=-1;
    for(int i=0;i<this->EmpNum;i++){
        if(this->EmpArray[i]->Id == id){
            index=i;
            break;
        }
    }
​
    return index;
}
​
void WorkerManager::Mod_Emp(){
    if(this->FileIsEmpty){
        cout<<"文件不存在或记录为空"<<endl;
    }
    else{
        cout<<"请输入修改职工编码:"<<endl;
        int id;
        cin>>id;
​
        int ret=this->IsExist(id);
        if(ret!=-1){
            delete this->EmpArray[ret];
​
            int newId=0;
            string newName="";
            int dSelect=0;
​
            cout<<"查到"<<id<<"号职工,请输入新职工号:"<<endl;
            cin>>newId;
​
            cout<<"请输入新姓名:"<<endl;
            cin>>newName;
​
            cout<<"请输入岗位:"<<endl;
            cout<<"1.普通职工"<<endl;
            cout<<"2.经理"<<endl;
            cout<<"3.老板"<<endl;
            cin>>dSelect;
​
            Worker * worker = NULL;
            switch(dSelect){
                case 1:
                    worker = new Employee(newId, newName, dSelect);
                    break;
                case 2:
                    worker = new Manager(newId, newName, dSelect);
                    break;
                case 3:
                    worker = new Boss(newId, newName, dSelect);
                    break;
                default:
                    break;
            
            }
            this->EmpArray[ret]=worker;
​
            cout<<"修改成功!"<<this->EmpArray[ret]->DeptId<<endl;
​
            this->save();
        }
        else{
            cout<<"修改失败,查无此人"<<endl;
        }
    }
     
    system("pause");
    system("cls"); 
}
​
void WorkerManager::Find_Emp(){
    if(this->FileIsEmpty){
        cout<<"文件不存在或者记录为空"<<endl;
    }
    else{
        cout<<"请输入查找的方式:"<<endl;
        cout<<"1.按职工编号查找"<<endl;
        cout<<"2.按姓名查找"<<endl;
​
        int select = 0;
        cin>>select;
​
        if(select==1){
            int id;
            cout<<"请输入查找的职工编号:"<<endl;
            cin>>id;
​
            int ret=IsExist(id);
            if(ret!=-1){
                cout<<"查找成功!该职工信息如下:"<<endl;
                this->EmpArray[ret]->showInfo();
            }
            else{
                cout<<"查找失败,查无此人"<<endl;
            }
        }
        else if(select==2){
            string name;
            cout<<"请输入查找的姓名:"<<endl;
            cin>>name;
​
            bool flag=false;
            for(int i=0;i<EmpNum;i++){
                if(EmpArray[i]->Name==name){
                    cout<<"查找成功,员工编号为:"
                        <<EmpArray[i]->Id
                        <<"号的信息如下:"<<endl;
                    
                    flag=true;
​
                    this->EmpArray[i]->showInfo();
                }
            }
            if(flag==false){
                cout<<"查找失败,查无此人"<<endl;
            }
        }
    }
​
    system("pause");
    system("cls");
}
​
void WorkerManager::Sort_Emp(){
    if(this->FileIsEmpty){
        cout<<"文件不存在或记录为空"<<endl;
        system("pause");
        system("cls");
    }
    else{
        cout<<"请选择排序方式:"<<endl;
        cout<<"1.按职工号进行升序"<<endl;
        cout<<"2.按职工号进行降序"<<endl;
​
        int select = 0;
        cin>>select;
​
        for(int i=0;i<EmpNum;i++){
            int minOrMax=i;
            for(int j=i+1;j<EmpNum;j++){
                if(select==1){
                    if(EmpArray[minOrMax]->Id>EmpArray[j]->Id){
                        minOrMax=j;
                    }
                }
                else{
                    if(EmpArray[minOrMax]->Id<EmpArray[j]->Id){
                        minOrMax=j;
                    }
                }
            }
            if(i!=minOrMax){
                Worker * temp = EmpArray[i];
                EmpArray[i]=EmpArray[minOrMax];
                EmpArray[minOrMax]=temp;
            }
        }
        cout<<"排序成功,排序后结果为:"<<endl;
        this->save();
        this->Show_Emp();
    }
}
​
void WorkerManager::Clean_File(){
    cout<<"确认清空?"<<endl;
    cout<<"1.确认"<<endl;
    cout<<"2.返回"<<endl;
​
    int select=0;
    cin>>select;
​
    if(select==1){
        ofstream ofs(FILENAME, ios::trunc);
        ofs.close();
​
        if(this->EmpArray!=NULL){
            for(int i=0;i<this->EmpNum;i++){
                if(this->EmpArray[i]!=NULL){
                    delete this->EmpArray[i];
                }
            }
            this->EmpNum=0;
            delete[] this->EmpArray;
            this->EmpArray=NULL;
            this->FileIsEmpty=true;
        }
        cout<<"清空成功"<<endl;
    }
    system("pause");
    system("cls");
}
​
WorkerManager::~WorkerManager(){
    if(this->EmpArray!=NULL){
        for(int i=0;i<this->EmpNum;i++){
            if(this->EmpArray[i]!=NULL){
                delete this->EmpArray[i];
            }
        }
        delete[] this->EmpArray;
        //this->EmpArray=NULL;
    }
​
}

课程链接:14 职工管理系统-显示职工功能_哔哩哔哩_bilibili

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值