一、内存分区模型
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个函数
-
默认构造函数
-
默认析构函数
-
默认拷贝构造函数
-
赋值运算符 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>
文件类型分为两种:
-
文本文件:文件以文件的ASCII码形式存储在计算机中
-
二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂他们
操作文件的三大类:
-
ofstream:写操作
-
ifstream:读操作
-
fstream:读写操作
8.1文本文件
写文件
-
包含头文件
#include<fstream>
-
创建流对象
ofstream ofs;
-
打开文件
ofs.open("文件路径",打开方式);
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意:文件打开方式可以配合使用,利用 | 操作符
-
写数据
ofs<<"写入的数据";
-
关闭文件
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;
}
读文件
-
包含头文件
#include<fstream>
-
创建流对象
ifstream ifs;
-
打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
-
写数据
四种方式读取
-
关闭文件
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;
}
}