内存分区模型
代码区:存放函数体的二进制代码,由操作系统进行管理。
全局区:存放全局变量和静态变量以及全局常量
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放程序结束时由操作系统回收。
内存四区存在的意义:
不同区域存放不同的数据,赋予不同的生命周期
程序运行前
在程序编译后,生成了exe可执行程序,未执行该区域前分为两个区域。
代码区:
存放cpu执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可,代码区是制度的,使其只读是为了防止程序意外的修改他的指令
全局区:
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此。
该区域的数据在程序结束后由操作系统释放。
程序运行后
栈区:
由编译器进行分配和释放。
不要返回局部变量的地址,栈区分配的数据由编译器自动释放。
#include <iostream>
using namespace std;
int* func(){
int a=10;
return &a;
}
int main(){
int * p=func();
cout << *p << endl;//第一次输出正确的值,因为编译器做了保留
cout << *p << endl;//第二次输出乱码,因为局部变量保存在栈中,栈中的数据编译器进行释放
system("pause");
return 0;
}
程序运行后
堆区
由于程序员分配释放,若程序员不释放,则程序结束后由操作系统回收和释放。
用new关键字在堆区进行开辟内存
int* func(){
/*
new 关键字用来在堆中开辟内存,new关键字返回的是一个地址
用指针来保存,指针是局部变量
*/
int * a=new int(10);
return a;
}
int main(){
int * p=func();
cout << *p << endl;//一直输出10
cout << *p << endl;//一直输出10,只要不被释放,只有当程序结束时由操作系统释放
system("pause");
return 0;
}
new操作符
c++中用new操作符在堆区开辟内存。 堆区开辟的内存手动开辟,手动释放,释放delete操作符。 利用new创建的数据,会返回该数据对应的类型的指针。
#include <iostream>
using namespace std;
int * func(){
int * a=new int(10);
return a;
}
void test01(){
int * p=func();
cout << *p<<endl;
cout << *p<<endl;//未释放就一直存在
delete p;//释放内存
cout << *p<<endl;//出现乱码
}
void test02(){
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;//释放数组内存
for(int i=0;i<10;i++){
cout << arr[i] << endl;
}
}
int main(){
test02();
system("pause");
return 0;
}
引用
引用就是给变量起别名
数据类型 &别名=原名;
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;
system("pause");
return 0;
}
注意事项
引用必须要初始化赋值。 引用一旦初始化后就不可以再更改。
int main(){
int a=10;
//int &b; 引用必须初始化
int &b=a;
int c=20;
b=c;// 这是赋值操作不是修改引用
cout << "a =" << a <<endl;
cout << "b =" << b <<endl;
cout << "c =" << c <<endl;
system("pause");
return 0;
}
引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修改实参, 优点:简化指针。
void swap(int &a,int &b){
// 利用引用接收传递过来的实参,进行修改实参
int temp=a;
a=b;
b=temp;
}
int main(){
int a=10;
int b=20;
swap(a,b);
cout << "a =" << a <<endl;
cout << "b =" << b << endl;
system("pause");
return 0;
}
引用做函数返回值
引用也可以做函数的返回值,但是注意不要返回局部变量的引用。
int & swap(){
static int a=10;//因为不能返回局部变量的引用,所以把变量提升为静态变量(静态变量保存在全局区)
return a;
}
int main(){
int &refa=swap();//因为返回了引用所以要用引用去接收
cout << "refa =" << refa <<endl;
cout << "refa =" << refa <<endl;
//函数最为左值,函数做为左值必须返回引用
swap()=1000;//因为返回的引用,所以这个赋值为1000相当于改的引用的值
cout << "refa =" << refa <<endl;
cout << "refa =" << refa <<endl;
system("pause");
return 0;
}
引用的本质
引用的本质在c++中内部实现是一个指针常量,
常量引用
常量引用主要用来形式形参,防止误操作
#include <iostream>
using namespace std;
void swap(const int &a){
cout <<a << endl;
a=1000;
//使用了常量引用之后就不可以再去进行修改
cout << a<< endl;
}
int main(){
int a=100;
swap(a);
system("pause");
return 0;
}
函数提高
函数的参数默认值
就是形参的默认值。 如果函数分开写,那么声明函数和实现函数只能有一个的参数写默认值。
#include <iostream>
using namespace std;
void add(int a,int b=20 ,int c=50){
// 一旦一个参数有了默认值,那么它之后的所有参数都必须要有默认值
cout << (a+b+c) << endl;
}
int main(){
add(10);//有默认值的参数,如果传了该参数就用传的参数,没有就用默认值
system("pause");
return 0;
}
函数的占位参数
c++的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
返回值类型 函数名(数据类型) {}
void add(int a,int){
cout << "this is Add" << endl;
}
int main(){
add(10,20);//占位参数一定要传递,否则无法调用该函数
system("pause");
return 0;
}
函数重载
作用名:函数名可以相同,提高复用性
满足条件
同一个作用域下
函数名称相同
函数参数类型不同或者个数不同或者顺序不同
void func(){
cout << "This is func ()" <<endl;
}
void func(int a){
cout << "This is func (int a)"<<endl;
}
void func(double a){
cout << "This is func(double a)" <<endl;
}
void func(int a,double b){
cout << "This is func(int a,double b)"<<endl;
}
void func(double b,int a){
cout << "This is func(double b,int a)"<<endl;
}
int main(){
/*
参数个数不同
func();
func(10);
*/
/*
参数类型不同
func(10)
func(3.14);
*/
/* 参数顺序不同
func(10,3.14);
func(3.14,10);
*/
system("pause");
return 0;
}
类和对象
面向对象的三大特性:封装,继承,多态 c++认为万物皆是对象,对象上有其属性和行为。
封装
将属性和行为作为一个整体。 将属性和行为加以权限控制。 class{权限 属性/方法}
#include <iostream>
using namespace std;
const double PI=3.14;
class Cricle{
public :
int r;
double compute(){
return 2*PI*r;
}
};
int main(){
//对类进行实例化,在c++中对类进行实例化不能使用new 关键字,因为new关键字是在堆中开辟内存的
/*
* 一个简单的猜想,因为c++算底层语言,js和java算是高级语言
因为js和java里去实例化类需要用到new关键字,然后在c++里用new关键字是在堆内存里去开辟空间的,js里的类的实例化是存在堆内存里的,node是用c++写的,所以js里去实例化一个类用new关键字,相当于告诉c++在堆内存里开辟一块空间,用来存储当前的对象。
*/
Cricle c1;
c1.r=10;
cout << c1.compute() << endl;
system("pause");
return 0;
}
访问权限
public 公共权限 类内可以访问,类外可以访问
private 私有权限 类内可以访问,类外不可以访问,子类不能访问
protected 保护权限 类内可以访问,类外不可以访问,子类可以访问
struct 和class的区别
struct和class的区别就在于默认的访问权限不同。
struct的默认权限是共有的 class 的默认权限是私有的
成员属性私有化
成员属性私有后,可以自己控制读写权限,对于写权限可以检验数据的有效性
class Person{
private :
string name;
int age;
public :
void setName(string names){
name=names;
};
string getName(){
return name;
};
void setAge(int num){
//控制数据的有效性
if(num < 0 || num >150){
cout << "you are dog ???" << endl;
return;
}else{
age=num;
}
}
int getAge(){
return age;
}
};
int main(){
Person p1;
p1.setName("Tom");
cout << p1.getName() << endl;
p1.setAge(10);
cout << p1.getAge() <<endl;
system("pause");
return 0;
}
//封装实战demo,
// 设计立方体类,求出立方体的面积和体积
// 分别利用全局函数和成员函数判断两个立方体是否相等
#include <iostream>
using namespace std;
#include <string>
const double PI = 3.14;
class Cube
{
private:
int l; //长
int w; //宽
int h; //高
public:
//设置和获取长宽高
void setL(int L) { l = L; }
void setW(int W) { w = W; }
void setH(int H) { h = H; }
int getL() { return l; }
int getW() { return w; }
int getH() { return h; }
//获取面积
int mj() { return ((2 * l * w) + (2 * w * h) + (2 * l * h)); }
//获取体积
int tj() { return l * h * w; }
//利用成员函数判断两个立方体是否相等
bool equal(Cube &c)
{
if ( l == c.getL() && h == c.getH() && w == c.getW())
{
return true;
}
else
{
return false;
}
}
};
//利用全局函数判断是否相等
bool isSem(Cube &c1, Cube &c2)
{
if (c1.getL() == c2.getL() && c1.getH() == c2.getH() && c1.getW() == c2.getW())
{
return true;
}
else
{
return false;
}
}
int main()
{
Cube c1;
c1.setH(10);
c1.setL(10);
c1.setW(10);
Cube c2;
c2.setH(10);
c2.setL(10);
c2.setW(10);
//成员函数判断
bool ret = c1.equal(c2);
if (ret)
{
cout << "c1 == c2" << endl;
}
else
{
cout << "c1 != c2 " << endl;
}
//全局函数判断
// bool ret = isSem(c1, c2);
// if (ret)
// {
// cout << "c1 == c2" << endl;
// }
// else
// {
// cout << "c1 != c2 " << endl;
// }
system("pause");
return 0;
}
对象的初始化和清理
构造函数和析构函数
构造函数和析构函数是编译器自动去调用的,如果开发不提供构造函数和析构函数那么编译器会自动实现的空实现。 构造函数:主要作用于在创建对象时为对象的成员属性赋值,构造函数由编译器自动调用。 析构函数:主要作用于在对象销毁前系统自动调用,执行一些清理工作
构造函数语法
类名(){}
构造函数,没有返回值也不写void
函数名称与类名相同
构造函数也可以有参数,因此可以发生重载
程序在实例对象时,会自动调用
析构函数语法: ~类名(){}
析构函数,没有返回值也不写void
函数名称与类名相同,在名称前加上符号~
析构函数不可以有参数,因此不可以发生重载
程序在对象销毁前自动调用析构,无须手动调用而且只会调用一次。
class Person{
public :
Person(){
cout << "this is gz" << endl;
}
~Person(){
cout << "this is jg" << endl;
}
};
void test01(){
Person p;
}
int main()
{
Person p1; // 因为main函数一直没有结束所以这里不会触发到析构函数
test01(); //在test方法里,会触发到析构函数,因为test函数执行完了,对象要被回收掉
system("pause");
return 0;
}
构造函数的分类及调用
按参数分为:有参构造和无参构造 按类型分为:普通构造和拷贝构造
调用方法:括号法,显示法,隐式转换法
注意事项:
不要利用拷贝构造函数,初始化匿名对象
不要用()调用无参构造函数
class Person{
public :
int age;
Person(){
//无参构造
cout << "this is gz" << endl;
}
Person(int a){
age=a;
cout << "yc" << endl;
//有参构造
}
Person(const Person &p){
age=p.age;
cout << "copy"<<endl;
//拷贝构造
}
};
void test01(){
//默认构造函数
Person p1;
//括号法
// Person p2(10);//调用有参构造函数
// Person p3(p2);//拷贝构造函数
//显示法
// Person p2=Person(10); //调用有参构造函数
// Person p3=Person(p2); // 调用拷贝构造函数
// 隐式调用法
Person p4=10; //调用有参的构造函数
Person p5=p4; //调用拷贝构造函数
}
int main()
{
test01();
system("pause");
return 0;
}
拷贝构造函数的调用时机
拷贝构造函数调用实际的三种情况:
使用一个已经创建完毕的对象来初始化一个新对象。
值传递的方式给函数参数传值
以值方式返回局部对象
class Person{
public :
int age;
Person(){
//无参构造
cout << "this is gz" << endl;
}
Person(int a){
age=a;
cout << "yc" << endl;
//有参构造
}
Person(const Person &p){
age=p.age;
cout << "copy"<<endl;
//拷贝构造
}
~Person(){
cout << "xg" <<endl;
}
};
// void test01(){
// //使用一个已经创建完毕的对象初始化一个新对象
// Person p1(10);
// Person p2(p1);
// }
// void doWork(Person p){
// }
// void test02(){
// //值传递的方法给函数参数传值
// Person p;
// doWork(p);
// }
Person doWork02(){
//以值方式返回局部对象
Person p1(10);
return p1;
}
void test03(){
Person p=doWork02();
cout << &p << endl;
}
int main()
{
test03();
system("pause");
return 0;
}
构造函数的调用规则
默认情况下 c++至少给一个类添加三个函数
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性值进行拷贝
构造函数的调用规则
如果用户定义有参构造函数,c++不在默认提供无参构造,但是会提供默认拷贝构造。
如果用户自定义拷贝构造函数,c++不会再提供其他构造函数。
深拷贝和浅拷贝
浅拷贝 : 简单的赋值拷贝操作 深拷贝 : 在堆区重新申请空间,进行拷贝操作
利用编译器提供的拷贝构造函数会做浅拷贝操作; 浅拷贝带来的问题就是,堆区的内存被重复释放(解决这个问题的办法就是重写拷贝构造函数)。
#include <iostream>
using namespace std;
#include <string>
const double PI = 3.14;
class Person{
public :
int age;
int *height;
Person(int a,int h){
height=new int(h);
age=a;
cout << "yc" << endl;
//有参构造
}
Person(const Person &p){
//重写拷贝构造函数,避免浅拷贝带来的问题
age=p.age;
height=new int(*p.height);
}
~Person(){
// 将最堆区开辟的数据进行释放掉
if(height != NULL){
delete height;
height=NULL;
}
cout << "xg" <<endl;
}
};
void test01(){
Person p1(10,160);
cout << "p1 this age" << p1.age << *p1.height <<endl;
Person p2(p1);
cout << "p2 this age" << p2.age << *p2.height <<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
初始化列表
c++提供了初始化列表语法,用来初始化属性 构造函数():属性1(值1),属性2(值2)...{}
class Person{
public :
int a;
int b;
int c;
Person():a(10),b(20),c(30){}
};
int main()
{
Person p;
cout << p.a << p.b << p.c << endl;
system("pause");
return 0;
}
//这种方法,很好的代替了传统的属性赋初始化值的操作
类对象作为类成员
c++中的成员可以是另一个类的对象,我们称该成员为对象成员。 当一个类中的成员是一个类时,会先执行成员类的构造再执行自身的构造
class Phone{
public :
string p_name;
Phone(string name):p_name(name){
cout << "phone gz"<<endl;
}
};
class Person{
public :
string name;
Phone iphone;
Person(string name,string phone):name(name),iphone(phone){
cout<<"p gz"<<endl;
}
};
int main()
{
Person p("Tom","iphone 14");
cout << p.name << "hava a" << p.iphone.p_name <<endl;
system("pause");
return 0;
}
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员变量:
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
静态成员函数:
所有对象共享同一个函数
静态成员函数只能访问静态成员变量
class Person{
public :
static string name;
static void func(){
cout << "func "<<endl;
}
};
int main()
{
//通过对象访问静态函数
Person p;
p.func();
// 通过类名访问静态函数
Person ::func();
system("pause");
return 0;
}
对象模型和this指针
成员变量和成员函数分开存储
在c++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
this指针
this指针指向被调用的成员函数所属的对象。 用途: 当形参和成员变量同名时,可用this指针来区分。 在类的非静态成员函数中可以返回对象本身。
#include <iostream>
using namespace std;
#include <string>
class Person{
public :
int age;
Person(int age){
//当形参和成员变量名称重复时用this区分
this->age=age;
// this是一个指针所以调用成员变量时需要用->
};
Person& addAge(Person &p){
this->age+=p.age;
//返回当前对象本身
return *this;
//因为this是一个指针所以需要用*来返回
}
};
void test01(){
Person p1(10);
Person p2(10);
//链式调用
p2.addAge(p1).addAge(p1).addAge(p1);
/**
链式调用解释
因为这里调用了p2这个对象的addAge方法,然后addAge方法返回了*this,this是一个指针保存了p2这个对象在内存里的地址。所以相当于addAge方法返回了p2这个对象然后又一次调用了p2这个对象的addAge方法。这个就是链式调用
*/
cout << "this p1 's age" << p1.age << endl;
cout << "this p2 's age" << p2.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
空指针访问成员函数
c++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性。
const修饰成员函数
常函数:
成员函数后加const 后称这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加mutable关键字,在常函数中怡然可以修改。
常对象
声明对象时在对象前加const称该对象为常对象
常对象只能调用常函数
class Person{
public :
mutable int age; //因为常函数不能修改成员属性,所以需要在属性前加mutable关键字
//在函数后加const称为常函数
void addAge() const {
this->age=20;
cout<< this->age << endl;
}
Person(){
}
};
void test01(){
const Person p; //对象前加const称为常对象
//常对象只能调用常函数
p.addAge();
}
int main()
{
test01();
system("pause");
return 0;
}
友元
在类里,有些私有属性也想让类外特殊的一些函数或者类访问,就需要用到友元。
友元的目的就是让一个函数或者类访问另一个类的私有成员
关键字为friend
友元的三种实现
全局函数做友元
类做友元
成员函数做友元
全局函数做友元
class Building{
/*
把全局函数的声明放在类里,并加上friend关键字
这个全局函数就叫做这个类的友元,可以访问这个类的私有属性
*/
friend void get_Gay(Building * build);
public :
string setting_room; //客厅
Building(){
setting_room="keting";
bad_room="woshi" ;
}
private :
string bad_room; //卧室
};
void get_Gay(Building * build){
cout << "global function " << build->setting_room <<endl;
cout << "global function " << build->bad_room <<endl;
}
void test01(){
Building building;
get_Gay(&building);
}
int main()
{
test01();
system("pause");
return 0;
}
类做友元
#include <iostream>
using namespace std;
#include <string>
class Building
{
//这个goodFriend类是Building类的友元,可以访问私有属性
friend class goodFriend;
public:
string setting_room; //客厅
Building();
private:
string bad_room; //卧室
};
class goodFriend
{
public:
Building *building;
goodFriend();
void visit();
} ;
// //类外写成员函数
void goodFriend ::visit()
{
cout << 1<<endl;
//这个函数访问building中的属性
cout << "this is goodFriend" << building->setting_room << endl;
cout << "this is goodFriend" << building->bad_room << endl;
};
// 类外写构造函数
Building ::Building(){
setting_room = "keting";
bad_room = "woshi";
}
goodFriend :: goodFriend(){
building=new Building;
}
void test01(){
goodFriend friends;
friends.visit();
}
int main()
{
test01();
system("pause");
return 0;
}
成员函数做友元
class Building;
/**
需要先声明Building类,因为在goodFriend类有成员是Building的指针。
而且goodFriend类里必须要实现在Building类之前,因为Building类里有
goodFriend的成员函数做友元,如果goodFriend实现在Building类之后,那么编译器则会找不到goodFriend类的成员函数则会报错。
*/
class goodFriend
{
public:
Building *building;
goodFriend();
void visit();
} ;
class Building
{
//goodFriend类下的vist的成员函数是Building类的友元,可以访问
friend void goodFriend::visit();
public:
string setting_room; //客厅
Building();
private:
string bad_room; //卧室
};
// //类外写成员函数
void goodFriend ::visit()
{
//这个函数访问building中的私有属性
cout << "this is goodFriend" << building->setting_room << endl;
cout << "this is goodFriend" << building->bad_room << endl;
};
// 类外写构造函数
Building ::Building(){
setting_room = "keting";
bad_room = "woshi";
}
goodFriend :: goodFriend(){
building=new Building;
}
void test01(){
goodFriend friends;
friends.visit();
}
int main()
{
test01();
system("pause");
return 0;
}
运算符重载
运算符重载是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
运算符重载函数的函数名是编译器规定的:operator+ 最后以为是运算符号,写什么运算符就是什么运算符的重载。
加号运算符重载
作用:实现两个自定义数据类型相加的运算。
class Person{
public :
int a;
int b;
//成员函数重载
// Person operator+(Person &p){
// Person temp;
// temp.a=this->a+p.a;
// temp.b=this->b+p.b;
// return temp;
// }
Person(){
a=10;
b=10;
}
};
//全局函数重载
Person operator+(Person p1,Person p2){
Person temp;
temp.a=p1.a+p2.a;
temp.b=p1.b+p2.b;
return temp;
}
void test01(){
Person p1;
Person p2;
//成员函数本质调用
// Person p3=p1.operator+(p2);
//成员函数简写调用
//Person p3=p1+p2;
//全局函数本质调用
//Person p3=operator+(p1, p2);
// 全局函数简写调用
Person p3=p1+p2;
cout << p3.a << p3.b <<endl;
//!!!!!运算符重载函数也可以发生函数重载
}
int main()
{
test01();
system("pause");
return 0;
}
/**
operator函数是编译器给到的,
*/
左移运算符重载
左移元素符号就是<< 重载左移运算符可以输出自定义类型
class Person{
friend ostream & operator<<(ostream &cout,Person p);
public :
Person(int a,int b):a(a),b(b){}
private :
int a;
int b;
};
//全局函数重载
ostream & operator<<(ostream &cout,Person p){
//左移运算符不能写成员函数重载,因为写成员函数重载实现不了cout在左侧
cout << "p.a =" << p.a<<"p .b="<<p.b;
return cout;
}
void test01(){
Person p(10,20);
cout << p << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
递增运算符重载
通过重载递增运算符,实现自己的整型数据。 前置递增返回引用,后置递增返回值
#include <iostream>
using namespace std;
#include <string>
class MyInteger{
friend ostream & operator<<(ostream &cout,MyInteger m);
public :
MyInteger(){num=0;}
//重载前置++
MyInteger & operator++(){
//先进行++运算,再返回自身
num++;
return *this;
}
//重置后置++
MyInteger operator++(int){
//因为后置++是先使用这个值,然后再++,所以我们需要先保存一下当前的值,然后再让当前的值++,最后返回记录的值
MyInteger temp=*this;
num++;
return temp;
}
private :
int num;
};
//全局函数重载左移
ostream & operator<<(ostream &cout,MyInteger m){
cout << m.num;
return cout;
}
void test01(){
MyInteger m;
cout << ++(++m) <<endl;
cout << m << endl;
MyInteger m2;
cout << m2++ << endl;
cout << m2 <<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
赋值运算符重载
赋值运算符重载主要用于类里在堆中的数据,存在内存重复释放的问题。
c++编译器至少给一个类添加4个函数
默认构造函数
默认析构函数
默认拷贝构造函数
赋值运算符 operator= 对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会产生深浅拷贝的问题。
class MyInteger{
public :
MyInteger(int num){
this->num=new int(num);
}
//析构函数用来释放在堆区的内存
~MyInteger(){
if(num != NULL){
delete num;
num=NULL;
}
}
int *num;//创建一个指针,用来指向存储在堆区的数据
MyInteger & operator=(MyInteger &m){
//判断是否有堆区内存,如果有先释放干净
if(num != NULL){
delete num;
num=NULL;
}
num=new int(*m.num);//重新开辟内存
//返回自身
return *this;
}
};
void test01(){
/* MyInteger m(18);
MyInteger m2=m; 使用自带的赋值操作= 会造成内存重复释放的问题,所有需要重载赋值操作符
cout << *m.num <<endl;
cout << *m2.num <<endl;
*/
MyInteger m(18);
MyInteger m2(20);
MyInteger m3(30);
m3=m2=m;
cout << *m.num <<endl;
cout << *m2.num <<endl;
cout << *m3.num <<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
关系运算符重载
重载关系运算符,可以让两个自定义的数据类型进行比较。
class Person{
public :
int age;
string name;
Person(int age,string name):age(age),name(name){}
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 true;
}
return false;
}
};
void test01(){
Person p1(18,"Tom");
Person p2(20,"Tom");
if(p1 == p2){
cout << "p2 and p1 yes equal" <<endl;
}else{
cout << "p2 and p1 No equal" <<endl;
}
if(p1 != p2){
cout << "p2 and p1 No equal" <<endl;
}else{
cout << "p2 and p1 Yes equal" <<endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
函数调用运算符重载
函数调用运算符() 也可以重载 由于重载之后的使用方式很像函数的调用,因此称为仿函数 仿函数没有固定写法,非常灵活
//一个输出类
class MyPrint{
public :
//重载函数调用
void operator()(string text){
cout << text <<endl;
}
};
// 一个加法类
class add{
public:
int operator()(int a,int b){
return a+b;
}
};
void test01(){
MyPrint m;
//这个就叫做仿函数
m("Hello World");
add a;
int ret=a(1,2);
cout << ret <<endl;
//匿名函数对象
cout <<add()(10,20)<<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
继承
有些类有很多共同的属性,也有自己的个性,所以他们共同的属性可以去继承同一个父类这就出现了继承。
c++继承的语法是这样的 class 子类 : 继承方式 父类{
}
class BasePage{
public :
void header(){
cout <<"index Login Router";
}
void footer(){
cout << "this is foooter";
}
}
class Java : public BasePage{
}
继承方式
继承方式有
公共继承 (在子类中依然是public的权限,子类不能访问私有属性)
保护继承 (父类的public 属性到子类中变为了保护权限,保护权限还是保护权限,不能访问私有属性)
私有继承 (父类的public 到子类中变为了私有属性)
继承中的构造和析构顺序
子类继承父类后,当创建子类对象,父类的构造函数也会执行。 先执行父类的构造函数,后执行子类的构造函数。先执行子类的析构函数,后执行子类的析构函数
class Base{
public :
Base(){
cout <<"Base gz"<<endl;
}
~Base(){
cout <<"Base xg" <<endl;
}
};
class Son : public Base {
public :
Son(){
cout <<"Son gz"<<endl;
}
~Son(){
cout << "Son xg"<<endl;
}
};
void test01(){
Son s1;
}
int main()
{
test01();
system("pause");
return 0;
}
同名成员处理
子类和父类中有同名成员属性或者函数。 访问子类同名成员,直接访问即可; 访问父类同名成员,加作用域。
作用域语法 子类.父类 :: 属性/函数
class Base{
public :
int a;
Base(){
a=100;
}
void get_A(){
cout <<"this is Base a="<<a<<endl;
}
};
class Son : public Base {
public :
int a;
Son(){
a=10;
}
void get_A(){
cout << "this is Son a="<<a<<endl;
}
};
void test01(){
Son s1;
cout << s1.a <<endl;
cout << s1.Base :: a <<endl;//访问父类中的同名成员属性需要加一个作用域
s1.get_A();
s1.Base::get_A(); //调用父类的同名成员函数也需要加一个作用域
}
int main()
{
test01();
system("pause");
return 0;
}
同名的静态成员
静态成员和非静态成员出现同名,处理方式一样
class Base{
public :
static int a;
Base(){}
static void get_A(){
cout <<"this is Base a="<<a<<endl;
}
};
int Base :: a=10;
class Son : public Base {
public :
static int a;
static void get_A(){
cout << "this is Son a="<<a<<endl;
}
};
int Son::a=100;
void test01(){
//通过对象访问
Son s1;
cout << s1.a <<endl;
cout << s1.Base :: a <<endl;//访问父类中的同名成员属性需要加一个作用域
s1.get_A();
s1.Base ::get_A();
//通过类名访问
cout << Son ::a<<endl;//子类静态成员
cout << Son :: Base :: a<<endl;//父类静态成员
Son::get_A();
Son ::Base::get_A();
}
int main()
{
test01();
system("pause");
return 0;
}
多继承语法
c++中允许一个类继承多个类 class 子类 : 继承方式 父类,继承方式,父类
多继承可能会引发父类中有同名成员出现,需要加作用域去区分。
c++不建议继承多个类
class Base{
public :
int a;
Base(){
a=10;
}
};
class Base2{
public :
int a;
Base2(){
a=100;
}
};
class Son : public Base ,public Base2{
public :
int a;
Son(){
a=200;
}
};
void test01(){
Son s;
//多个继承带来的问题就是,当多个父类都含有同名属性时,需要加作用域区分
cout << s.a <<endl;
cout << s.Base ::a <<endl;
cout << s.Base2 ::a <<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
菱形继承
菱形继承的概念 两个子类同时继承了同一个父类,然后一个孙子类继承了两个子类。这种结构就是菱形继承。
菱形继承带来的问题:
孙子类继承两个父类,两个父类又都继承了祖先所以孙子类身上相当于继承了两份祖先类的数据。造成资源浪费。
利用虚继承可以解决这个问题
继承之间加上关键字 virtual 变为虚继承
class Animal{
public :
int age;
};
class shiep :virtual public Animal{}; //使用虚继承
class tuo :virtual public Animal{}; // 使用虚继承
class ytuo :public shiep,public tuo{};
void test01(){
ytuo yt;
//因为两个父类都有同名的数据所以需要用作用域去区分
yt.shiep::age=18;
cout << "this is shiep " << yt.shiep::age <<endl;
cout << "this is tuo" << yt.tuo::age<<endl;
//但是目前ytuo 这个类的age数据没有办法设置了,因为有两份数据,所以需要用到虚继承去解决这个问题
cout << "this is ytuo"<<yt.age << endl;
//虚继承的原理是,继承了一个指针,用指针来找到具体的数据
}
int main()
{
test01();
system("pause");
return 0;
}
多态
多态是面向对象的三大特性之一
多态分为两类
静态多态:函数冲澡,运算符重载,复用函数名
动态多态: 子类和虚函数实现运行时
静态多态和动态多态的区别:
静态多态的函数地址早绑定:编译阶段确定函数地址
动态多态的函数地址晚绑定:运行阶段确定函数地址
动态多态满足条件:
1.有继承关系
2.子类要重写父类的虚函数
动态多态的调用
父类的指针或者引用,执行子类对象
多态的优点:
代码组织结构清晰
代码可利用性强
利于前期和后期维护
class Animal{
public :
virtual void speak(){
cout << "Animal speaking" <<endl;
}
};
class Cat :public Animal{
public :
void speak(){
cout << "Cat speaking" <<endl;
}
};
class Dog :public Animal{
public :
void speak(){
cout <<"Dog speaking" <<endl;
}
};
void doSpeak(Animal &animal){ // Animal &animal =cat
animal.speak();
}
void test01(){
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
int main()
{
test01();
system("pause");
return 0;
}
//多态实战demo,分别利用普通实现和多态实现
//普通实现
class compuate{
public :
int a;
int b;
compuate(int a,int b):a(a),b(b){};
int getResult(string type){
if(type == "+"){
return a+b;
}else if(type == "-"){
return a-b;
}else if(type == "*"){
return a*b;
}else{
return 0;
}
//如果想继续扩展功能则需要修改源码
// 但是真实的开发中,提倡 括开修闭原则
// 就是允许扩展开发,但是禁止修改
};
};
//多态实现
//设计一个计算器抽象类
class ComPuate{
public :
int a;
int b;
virtual int getResult(){return 0;}
};
//实现一个加法类
class jia :public ComPuate{
public :
int getResult(){
return a+b;
}
};
//实现一个减法类
class jian:public ComPuate{
public :
int getResult(){
return a-b;
}
};
void test01(){
//使用多态
ComPuate *abc=new jia;//指针或引用指向子类
abc->a=10;
abc->b=20;
cout << "a+b =" <<abc->getResult()<<endl;
//因为是在堆中开辟的内存,所以用完后立即销毁
delete abc;
abc=NULL;
//减法
ComPuate *adc=new jian;
adc->a=20;
adc->b=10;
cout << "a-b =" <<adc->getResult()<<endl;
delete adc;
adc=NULL;
}
int main()
{
test01();
system("pause");
return 0;
}
纯虚函数和抽象类
在多态中,通常父类中的虚函数都是无用的,调用的都是子类的函数。因此可以将函数改为纯虚函数。 纯虚函数的语法 virtual 返回值类型 函数名(参数)=0
如果一个类中与纯虚函数,那么这个类称为抽象类
抽象类无法被实例化 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//抽象类
class Base{
public :
virtual void func()=0; //纯虚函数
};
class Son:public Base{
public :
void func(){//子类必须实现抽象类的纯虚拟函数
cout << "this is Son func"<<endl;
}
};
void test01(){
//Base b;//抽象类无法实例化
Base * abc=new Son;
abc->func();
delete abc;
abc=NULL;
}
int main()
{
test01();
system("pause");
return 0;
}
// 抽象类实战 制作饮品
//饮品基类
class yin{
public :
//煮水
virtual void zhuWrite()=0;
// 冲泡
virtual void chongpao()=0;
// 加入辅料
virtual void add()=0;
//倒入杯中
virtual void dao()=0;
//制作
void mark(){
zhuWrite();
chongpao();
add();
dao();
}
};
//咖啡类
class coffee :public yin{
public :
void zhuWrite(){
cout << "煮开水"<<endl;
}
void chongpao(){
cout << "倒入速溶咖啡"<<endl;
}
void add(){
cout << "加奶和加糖" <<endl;
}
void dao(){
cout << "倒入咖啡杯" <<endl;
}
};
//咖啡类
class tea :public yin{
public :
void zhuWrite(){
cout << "煮开水"<<endl;
}
void chongpao(){
cout << "倒入茶叶"<<endl;
}
void add(){
cout << "加奶" <<endl;
}
void dao(){
cout << "倒入茶杯" <<endl;
}
};
//工作台
void doWork(yin *abc){
abc->mark();
delete abc;
};
//前台
void test01(){
//咖啡单传入工作台
doWork(new coffee);
cout << "~~~~~~~~~~~~~~~"<<endl;
//茶单传入工作台
doWork(new tea);
}
int main()
{
test01();
system("pause");
return 0;
}
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针无法调用到子类的析构代码。
解决方式,将父类的析构函数改为虚析构或者纯虚析构
class Animal{
public:
Animal(){
cout << "Animal gz" <<endl;
}
virtual void speak()=0;
//写成虚函数
// virtual ~Animal(){
// cout << "Animal xg"<<endl;
// }
//写成纯虚函数的话要在类外去实现这个纯虚函数
virtual ~Animal()=0;
};
Animal ::~Animal(){
cout << "Animal xg"<<endl;
}
class Cat : public Animal{
public:
Cat(string name){
this->name=new string(name);
};
void speak(){
cout << *name <<endl;
}
~Cat(){
cout << "Cat xg"<<endl;
if(name != NULL){
delete name;
name=NULL;
}
}
string *name;
};
void test01(){
Animal *cat=new Cat("Tom");
cat->speak();
delete cat;
}
int main()
{
test01();
system("pause");
return 0;
}
//多态实战,电脑组装
//三个零件的基类
class Cpu{
public:
virtual void cpuWork()=0;
};
class VideoCard{
public:
virtual void videoCardWork()=0;
};
class NeiCun{
public:
virtual void neicunWork()=0;
};
//计算机类
class Compater{
public:
Cpu *cpu;
VideoCard *videoCard;
NeiCun *neicun;
bool state;
Compater(Cpu *cpu,VideoCard *videoCard,NeiCun *neicun){
this->cpu=cpu;
this->videoCard=videoCard;
this->neicun=neicun;
}
void Work(){
//开始工作将状态设置为true
state=true;
cout << "begin Work"<<endl;
cpu->cpuWork();
videoCard->videoCardWork();
neicun->neicunWork();
cout << "end Work"<<endl;
state=false;//结束工作将状态设置为false
}
~Compater(){
//根据工作状态释放内存中的数据
if(state == false){
//工作结束
delete cpu;
cpu=NULL;
delete videoCard;
videoCard=NULL;
delete neicun;
neicun=NULL;
}
}
};
//Intel品牌的三个零件
class cpuIntel:public Cpu{
public:
void cpuWork(){
cout << "Intel cpu working"<<endl;
}
};
class videoCardIntel:public VideoCard{
public:
void videoCardWork(){
cout << "Intel videoCard working"<<endl;
}
};
class NeiCunIntel:public NeiCun{
public:
void neicunWork(){
cout << "Intel neicun working"<<endl;
}
};
//Leveo品牌的三个零件
class cpuLeveo:public Cpu{
public:
void cpuWork(){
cout << "Leveo cpu working"<<endl;
}
};
class videoCardLeveo:public VideoCard{
public:
void videoCardWork(){
cout << "Leveo videoCard working"<<endl;
}
};
class NeiCunLeveo:public NeiCun{
public:
void neicunWork(){
cout << "Leveo neicun working"<<endl;
}
};
void test01(){
Compater * c1= new Compater(new cpuIntel,new videoCardIntel,new NeiCunIntel);
c1->Work();
//工作完之后释放内存
delete c1;
Compater *c2 =new Compater(new cpuLeveo,new videoCardLeveo,new NeiCunLeveo);
c2->Work();
delete c2;
}
int main()
{
test01();
system("pause");
return 0;
}
文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放,通过文件可以将数据持久化。 操作文件需要包含头文件< fstream >
文件类型分为两种: 文件以文本的ASCLL码形式存储在计算机中。 二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们。
操作文件的三大类:
ofstream 写操作
ifsream 读操作
fstream 读写操作
文本文件
写文件
写文件步骤:
包含头文件 #include <fstream>
创建流对象 ofstream ofsl
打开文件 ofs.open(文件路径,打开方式)
写数据 ofs << "数据"
关闭文件 ofs.close()
文件的打开方式:
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除再创建
ios::binary 二进制方式
文件打开方式可以组合使用,利用|操作符
例子:用二进制方式写文件 ios::binary|ios::out
#include <fstream> //头文件
void test01(){
ofstream ofs;//创建流对象
ofs.open("test.txt",ios::out|ios::trunc);//打开文件,(路径默认是当前文件夹下,打开方式是写文件和如果文件存在删除再创建)
ofs <<"张三"<<endl;//写入数据
ofs <<"年龄:21"<<endl;//写入数据
ofs <<"成绩:26"<<endl;//写入数据
ofs.close();//关闭文件
}
int main()
{
test01();
system("pause");
return 0;
}
读文件
读文件步骤
包含头文件 $include<fstream>
创建流对象 ifstream ifs;
打开文件并判断文件是否打开成功 ifs.open(文件路径,打开方式)
读数据:四种方式读取
关闭文件
#include <string>
#include <fstream>
void test01(){
ifstream ifs;//创建读文件的流对象
ifs.open("test.txt",ios::in);//打开文件
if(!ifs.is_open()){
cout << "No file"<<endl;
return;
}
//读文件
//第一种方式 字符数组
// char buf[1024]={0};
// while(ifs >> buf){ //将内容输入到buf字节数组中,文件末尾时返回false;
// cout << buf<<endl;
// }
//第二种方式
// char buf[1024]={0};
// while(ifs.getline(buf,sizeof(buf))){ //同样将数据输入到字符数组里,末尾时返回false
// cout <<buf<<endl;
// }
// 第三种方式
// string str;
// while(getline(ifs,str)){ //逐行读取到string中
// cout << str<<endl;
// }
// 第四种方式
// char c; //单个字符读取
// while((c=ifs.get()) != EOF){
// cout <<c;
// }
ifs.close();//关闭文件
}
int main()
{
test01();
system("pause");
return 0;
}
二进制文件
二进制文件,打开方式要指定为ios::binary
写文件
写文件主要利用流对象的成员函数write 函数原型:ostream& write(const char *buffer,len); 字符指针buffer指向内存中一段存储空间,len是读写的字节数
ofstream ofs;//创建写文件的流对象
ofs.open("Person.txt",ios::out | ios::binary);//打开文件
//创建要写的对象
Person p("张三",18);
//写入文件
ofs.write((const char *)&p,sizeof(p));
ofs.close();//关闭文件
读文件
读文件主要利用成员函数read 函数原型 istream& read(cahr *buffer,len); 字符指针buffer指向内存中一段存储空间,len是要读多少字节
class Person{
public :
char name[64];
int age;
Person(){}
};
void test01(){
ifstream ifs;//创建写文件的流对象
ifs.open("Person.txt",ios::in | ios::binary);//打开文件
//判断文件是否打开成功
if(!ifs.is_open()){
cout << "No File "<<endl;
}
Person p;
ifs.read((char *)&p,sizeof(Person));
cout << p.name <<endl;
cout << p.age <<endl;
ifs.close();//关闭文件
}
int main()
{
test01();
system("pause");
return 0;
}
/*这一章节内容实战,职工管理系统
公司中职工分为三类 :普通员工,经理,老板,显示信息时:显示职工编号,职工姓名,职工岗位。职工职责,任务
经理职责:完成老板的任务,并下发给员工
老板职责:管理公司所有事务
系统需要是实现以下功能
退出管理程序:退出当前管理系统
增加职工信息:批量添加职工信息,并将信息录入到问价中
显示职工信息:显示公司内部所有职工的信息
删除职工信息:按照编号删除指定的职工
修改职工信息:按照编号修改职工信息
查找职工信息,按照职工的编号或者姓名查找相关人员信息
按照编号排序:根据职工编号,进行排序
清空所有文档
*/
//首先需要一个主文件来运行程序
// mian.cpp
#include <iostream>
using namespace std;
#include "../includes/workerManger.h" //职工信息管理类
int main()
{
WorkerManger w;
int choice = 0; //用户的选择
while (true)
{
w.showMenu(); //显示菜单
cout << "Please enter your choice" << endl;
cin >> choice;
switch (choice)
{
case 0: //退出系统
w.exitSystem();
break;
case 1: //添加职工信息
w.addWorker();
break;
case 2: //显示职工信息
w.showWorker();
break;
case 3: //删除职工信息
{
cout << "Please enter the employee number you want to delete" << endl;
int id;
cin >> id;
w.delWorker(id);
}
break;
case 4: // 修改职工信息
{
cout << "Please enter the employee number you want to update" << endl;
int id;
cin >> id;
w.updateWorker(id);
}
break;
case 5: //查找职工信息
w.findWorker();
break;
case 6: // 按照编号排序
w.sortWorker();
break;
case 7: //清空所有信息
w.clearFile();
break;
default:
system("cls");
break;
}
}
system("pause");
return 0;
}
//因为是职工管理,那么不管什么职位,所有的职工都有共同的属性和行为,所以创建一个员工的基类,(这个基类的所有方法和属性都是由子类去实现的,所以只需要一个头文件即可)
// Worker.h
#pragma once
#include <iostream>
using namespace std;
#include <string>
//职工抽象基类
class Worker{
public:
virtual void showInfo()=0; //显示员工信息
virtual string getDeptName()=0; //获取职工岗位
int id;//编号
string name; //职工姓名
int deptId;//职工所在部门编号
};
//然后来进行子类的实现,子类分别是员工类,经理类,老板类
//员工类的头文件
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include "./Worker.h"
//普通员工类
class Employee:public Worker{
public :
Employee(string name,int id,int deptid);//构造函数
void showInfo();
string getDeptName();
};
//员工类的源文件
#include "../includes/Employee.h"
Employee::Employee(string name,int id,int deptid){
this->name=name;
this->id=id;
this->deptId=deptid;
}
void Employee::showInfo(){
cout << "Name :" << this->name;
cout << "\tID :" <<this->id;
cout << "\tdeptID :" <<this->deptId;
cout << "\tposts :" << this->getDeptName();
cout << endl;
}
string Employee::getDeptName(){
return "employee";
}
//经理类的头文件
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include "./Worker.h"
//经理类类
class Manger:public Worker{
public :
Manger(string name,int id,int deptid);//构造函数
void showInfo();
string getDeptName();
};
//经理类的源文件
#include "../includes/Manger.h"
Manger::Manger(string name,int id,int deptid){
this->name=name;
this->id=id;
this->deptId=deptid;
}
void Manger::showInfo(){
cout << "Name :" << this->name;
cout << "\tID :" <<this->id;
cout << "\tdeptID :" <<this->deptId;
cout << "\tposts :" << this->getDeptName();
cout << endl;
}
string Manger::getDeptName(){
return "manger";
}
//老板类的头文件
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include "./Worker.h"
//老板类
class Boss:public Worker{
public :
Boss(string name,int id,int deptid);//构造函数
void showInfo();
string getDeptName();
};
//老板类的源文件
#include "../includes/Boss.h"
Boss::Boss(string name,int id,int deptid){
this->name=name;
this->id=id;
this->deptId=deptid;
}
void Boss::showInfo(){
cout << "Name :" << this->name;
cout << "\tID :" <<this->id;
cout << "\tdeptID :" <<this->deptId;
cout << "\tposts :" << this->getDeptName();
cout << endl;
}
string Boss::getDeptName(){
return "boss";
}
//最后我们需要一个职工管理信息类,来进行系统的整个运行
//职工关系信息类的头文件 workerManger.h
#pragma once
#include <iostream>
#include "../includes/Worker.h"
using namespace std;
#define FileName "employee.txt"
#include <fstream>
class WorkerManger{
public:
WorkerManger();
void showMenu();//显示菜单
void exitSystem(); //退出系统
~WorkerManger();
int workerSize ; //职工人数
Worker ** workerArray; //职工数组指针
void addWorker(); //添加职工
void save(); //因为数据在程序结束的时候将会释放掉,所以我们把数据保存在文件中
bool fileIsEmpty;
int getNum(); //获取到文件中已经有的职工人数
void initWorker();// 初始化职工的堆数组
void showWorker();//显示所有的员工
void delWorker(int id);//删除职工
int isExist(int id);//判断职工是否存在
void updateWorker(int id);//根据编号修改职工信息
void findWorker();//查找职工
void sortWorker();//按照编号排序
void sort(int select,int order);//排序
void clearFile();
};
//职工管理信息类的函数实现
#include "../includes/workerManger.h"
#include "../includes/Employee.h" //员工类
#include "../includes/Manger.h" //经理类
#include "../includes/Boss.h" //老板类
WorkerManger::WorkerManger()
{
/**
*
* 因为我们不能每次打开程序的时候都去重新存数据,我们要考虑到
* 当文件存在的时候,读取文件中的数据存储到堆里
* 文件未创建的时候
* 文件不存在的时候
*/
ifstream ifs;
ifs.open(FileName, ios::in);
//当文件不存在的时候
if (!ifs.is_open())
{
this->fileIsEmpty = true;
this->workerSize = 0;
this->workerArray = NULL;
return;
}
//文件为空的情况
char ch;
ifs >> ch;
if (ifs.eof())
{
this->fileIsEmpty = true;
this->workerSize = 0;
this->workerArray = NULL;
return;
}
//当文件存在,且记录的有数据
this->workerSize = this->getNum();
//开辟新的空间
this->workerArray = new Worker *[this->workerSize];
//初始化职工数组
this->initWorker();
this->fileIsEmpty = false;
};
WorkerManger::~WorkerManger()
{
if (this->workerArray != NULL)
{
for (int i = 0; i < this->workerSize; i++)
{
if (this->workerArray[i] != NULL)
{
delete this->workerArray[i]; //清空每一项堆里的数据
}
}
this->workerSize = 0; //人数置为0
delete[] this->workerArray;
this->workerArray = NULL;
this->fileIsEmpty = true;
}
}
void WorkerManger::showMenu()
{
cout << "***********************" << endl;
cout << "******welcome to Worker Info Manger System******" << endl;
cout << "******0.Exit the hypervisor******" << endl;
cout << "******1.Increase employee information******" << endl;
cout << "******2.Displays employee information******" << endl;
cout << "******3.Delete a former employee******" << endl;
cout << "******4.Modify employee information******" << endl;
cout << "******5.Find employee information******" << endl;
cout << "******6.Sort by number******" << endl;
cout << "******7.Clear all information******" << endl;
cout << "***********************" << endl;
}
//退出系统
void WorkerManger::exitSystem()
{
cout << "Welcome to use it next time" << endl;
system("pause");
exit(0);
}
//批量添加职工
void WorkerManger::addWorker()
{
cout << "Please enter the number of people you want to add" << endl;
int size = 0;
cin >> size; //获取用户新添加的人数
if (size > 0)
{
// 原来的人数加上新人数,重新计算堆空间的大小
int newSize = this->workerSize + size;
// 开辟新的堆空间
Worker **newSpace = new Worker *[newSize];
//将原来的人数先添加到新的空间里
if (this->workerArray != NULL)
{
for (int j = 0; j < this->workerSize; j++)
{
newSpace[j] = this->workerArray[j];
}
}
//添加新的数据
for (int i = 0; i < size; i++)
{
int id; //员工编号
string name; //员工姓名
int select; //职位选择
cout << "Please enter " << i + 1 << " employee's name" << endl;
cin >> name;
cout << "Please enter " << i + 1 << " employee's Id" << endl;
cin >> id;
cout << "Please select an employee position" << endl;
cout << "****1.employee****" << endl;
cout << "****2.manger****" << endl;
cout << "****3.Boss****" << endl;
cin >> select;
Worker *worker = NULL;
switch (select)
{
case 1:
worker = new Employee(name, id, 1);
break;
case 2:
worker = new Manger(name, id, 2);
break;
case 3:
worker = new Boss(name, id, 3);
break;
default:
break;
}
//将数据放到数组里
newSpace[i + this->workerSize] = worker;
}
//释放原有内存空间
delete[] this->workerArray;
//更改空间指向
this->workerArray = newSpace;
//修改人数
this->workerSize = newSize;
//提示添加成功
cout << "Added successfully" << endl;
//更改文件状态不为空
this->fileIsEmpty = false;
//将数据保存到文件中
this->save();
}
else
{
cout << "Input error" << endl;
}
system("pause");
system("cls");
}
//将数据保存到文件中
void WorkerManger::save()
{
//创建数据流对象
ofstream ofs;
//打开文件
ofs.open(FileName, ios::out);
//写入数据
for (int i = 0; i < this->workerSize; i++)
{
ofs << this->workerArray[i]->name << " "
<< this->workerArray[i]->id << " "
<< this->workerArray[i]->deptId << endl;
}
//关闭文件
ofs.close();
}
int WorkerManger::getNum()
{
ifstream ifs;
ifs.open(FileName, ios::in);
string name;
int id;
int deptid;
int num = 0;
while (ifs >> name && ifs >> id && ifs >> deptid)
{
num++;
}
ifs.close();
return num;
}
void WorkerManger::initWorker()
{
ifstream ifs;
ifs.open(FileName, ios::in);
string name;
int id;
int deptid;
int index = 0;
while (ifs >> name && ifs >> id && ifs >> deptid)
{
Worker *worker = NULL;
if (deptid == 1)
{
worker = new Employee(name, id, deptid);
}
else if (deptid == 2)
{
worker = new Manger(name, id, deptid);
}
else
{
worker = new Boss(name, id, deptid);
}
this->workerArray[index] = worker;
index++;
}
ifs.close();
}
void WorkerManger::showWorker()
{
if (this->fileIsEmpty)
{
cout << "No File || File Null" << endl;
}
else
{
for (int i = 0; i < this->workerSize; i++)
{
this->workerArray[i]->showInfo();
}
}
system("pause");
system("cls");
}
void WorkerManger::delWorker(int id)
{
int index = this->isExist(id);
if (this->fileIsEmpty)
{
cout << "No File || File Null" << endl;
}
else
{
if (index == -1)
{
cout << "No such person was found" << endl;
}
else
{
//删除数组中的某一项,删除数组中的某一项实际上就是将数组进行前移
for (int i = index; i < this->workerSize - 1; i++)
{
this->workerArray[i] = this->workerArray[i + 1];
}
this->workerSize--;
cout << "Delete Employee Success " << endl;
this->save(); //同步到文件
}
}
system("pause");
system("cls");
}
int WorkerManger::isExist(int id)
{
int index = -1;
for (int i = 0; i < this->workerSize; i++)
{
if (this->workerArray[i]->id == id)
{
return i;
}
}
return index;
}
void WorkerManger::updateWorker(int id)
{
if (this->fileIsEmpty)
{
cout << "No File || File Null" << endl;
}
else
{
int index = this->isExist(id);
if (index != -1)
{
//首先将数组里的指针指向的堆里的数据删除
delete this->workerArray[index];
//让用户重新输入一遍数据
int id; //员工编号
string name; //员工姓名
int select; //职位选择
cout << "Please enter employee's name" << endl;
cin >> name;
cout << "Please enter employee's Id" << endl;
cin >> id;
cout << "Please select an employee position" << endl;
cout << "****1.employee****" << endl;
cout << "****2.manger****" << endl;
cout << "****3.Boss****" << endl;
cin >> select;
Worker *worker = NULL;
switch (select)
{
case 1:
worker = new Employee(name, id, 1);
break;
case 2:
worker = new Manger(name, id, 2);
break;
case 3:
worker = new Boss(name, id, 3);
break;
default:
break;
}
this->workerArray[index] = worker;
//同步保存数据到文件
this->save();
cout << "The modification was successful" << endl;
}
else
{
cout << "No such person was found" << endl;
}
}
system("pause");
system("cls");
}
void WorkerManger::findWorker()
{
if (this->fileIsEmpty)
{
cout << "No File || File Null" << endl;
}
else
{
//选择查找方式
cout << "Please enter how to find it" << endl;
cout << "******1.Find by name******" << endl;
cout << "******2.Find by Id******" << endl;
int f;
cin >> f;
if (f == 1)
{
string name;
cout << "Please enter the employee name you want to find" << endl;
cin >> name;
for (int i = 0; i < this->workerSize; i++)
{
if (this->workerArray[i]->name == name)
{
this->workerArray[i]->showInfo();
}
}
}
else if (f == 2)
{
cout << "Please enter the employee number you want to find" << endl;
int id;
cin >> id;
int index = this->isExist(id);
if (index != -1)
{
this->workerArray[index]->showInfo();
}
else
{
cout << "No such person was found" << endl;
}
}
}
system("pause");
system("cls");
}
void WorkerManger::sortWorker()
{
if (this->fileIsEmpty)
{
cout << "No File || File Null" << endl;
}
else
{
cout << "Please enter a sort method" << endl;
cout << "******1.ascending order******" << endl;
cout << "******2.Descending******" << endl;
int select;
cin >> select;
this->sort(select, 1); // 1是快排,2是冒泡排序
cout << "Sorting successful" << endl;
this->save(); //结果保存在文件中
system("pause");
system("cls");
}
}
void WorkerManger::sort(int select, int order)
{
int length = this->workerSize - 1;
if (order == 2)
{
//冒泡排序
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length - i; j++)
{
if (select == 1) // 升序
{
if (this->workerArray[j]->id > this->workerArray[j + 1]->id)
{
Worker *temp = this->workerArray[j];
this->workerArray[j] = this->workerArray[j + 1];
this->workerArray[j + 1] = temp;
}
}
else
{ //降序
if (this->workerArray[j]->id < this->workerArray[j + 1]->id)
{
Worker *temp = this->workerArray[j];
this->workerArray[j] = this->workerArray[j + 1];
this->workerArray[j + 1] = temp;
}
}
}
}
}
else
{
//快速排序
for (int i = 0; i < length + 1; i++)
{
//默认选择第一项为基准
int minOrMax = i;
for (int j = i + 1; j < length + 1; j++)
{
if (select == 1)
{
if (this->workerArray[minOrMax]->id > this->workerArray[j]->id)
{
minOrMax = j;
}
}
else
{
if (this->workerArray[minOrMax]->id < this->workerArray[j]->id)
{
minOrMax = j;
}
}
}
if (i != minOrMax)
{
Worker *temp = this->workerArray[minOrMax];
this->workerArray[minOrMax] = this->workerArray[i];
this->workerArray[i] = temp;
}
}
}
}
void WorkerManger::clearFile()
{
cout << "Whether to confirm that you want to clear it" << endl;
cout << "******1.Confirm******" << endl;
cout << "******2.exit******" << endl;
int confirm;
cin >> confirm;
if (confirm == 1)
{
ofstream ofs;
//如果文件存在先删除再创建,相当于做了清空操作
ofs.open(FileName, ios::trunc);
ofs.close();
//将所有的数据置为空
if (this->workerArray != NULL)
{
for (int i = 0; i < this->workerSize; i++)
{
if (this->workerArray[i] != NULL)
{
delete this->workerArray[i]; //清空每一项堆里的数据
}
}
this->workerSize = 0; //人数置为0
delete[] this->workerArray;
this->workerArray = NULL;
this->fileIsEmpty = true;
}
cout << "Emptying successful" << endl;
}
system("pause");
system("cls");
}