内存分区模型
- 运行前的区域:代码区、全局区
- 运行后的区域:栈区、堆区
代码区
存放函数体的二进制代码,特点是共享和只读
全局区
存放全局变量、静态变量(在普通变量前面加上static关键字)和常量(包括字符串常量和const修饰的全局变量),全局区的数据在程序结束后系统释放
#include <iostream>
#include <string>
using namespace std;
//全局变量
int g_a = 10;
int g_b = 10;
//全局常量
const int c_g_a = 10;
const int c_g_b = 10;
int main()
{
//局部变量
int a = 10;
int b = 10;
cout << "局部变量a: " << int(&a) << endl;
cout << "局部变量b: " << int(&b) << endl;
//局部常量
const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量a: " << int(&c_l_a) << endl;
cout << "局部常量b: " << int(&c_l_b) << endl;
//全局变量
cout << "全局变量a: " << int(&g_a) << endl;
cout << "全局变量b: " << int(&g_b) << endl;
//静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量a: " << int(&s_a) << endl;
cout << "静态变量b: " << int(&s_b) << endl;
//全局常量
cout << "全局常量a: " << int(&c_g_a) << endl;
cout << "全局常量b: " << int(&c_g_b) << endl;
//字符串常量
//注: 若写成string s = "hello world";此时s是局部变量,注意区分
cout << "字符串常量s: " << int(&"hello world") << endl;
system("pause");
return 0;
}
局部变量a: 5242580
局部变量b: 5242568
局部常量a: 5242556
局部常量b: 5242544
全局变量a: 10010624
全局变量b: 10010628
静态变量a: 10010632
静态变量b: 10010636
全局常量a: 10001200
全局常量b: 10001204
字符串常量s: 10001388
栈区
由编译器自动分配释放,存放函数的参数值、局部变量等,栈区的数据在函数执行完后自动释放。
堆区
由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收,利用new在堆区开辟内存,利用delete释放内存
new开辟数据
#include<iostream>
using namespace std;
int* func()
{
//new开辟数据到堆区
int *p = new int(10);
return p;
}
int main()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
}
delete释放内存
删除指针:delete p;
删除数组:delete [] arr;
引用
语法:数据类型 &别名 = 原名
目的:给变量起别名,别名和本名所指的内存空间是一致的,因此修改本名/别名都会引起内存数据的变化,所以就有下面的:用别名修改变量值、用形参修饰实参(类似指针的效果)、引用做函数返回值(左值)修改变量值。
本质:引用本质就是指针常量,即初始化后的指向地址不可修改,而内存数据可以修改,类似int * const p
注意:引用必须初始化(如int &b;是错误的),且初始化后不可以改变(指针常量,体现了本质)
int a = 10;
int& b = a;
引用做函数参数
#include<iostream>
using namespace std;
//值传递
void swap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "swap01" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
//地址传递
void swap02(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
cout << "swap02" << endl;
cout << "a = " << *a << endl;
cout << "b = " << *b << endl;
}
//引用传递
void swap03(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
cout << "swap03" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main()
{
int a = 10;
int b = 20;
//值传递
//swap01(a, b);
//地址传递
//swap02(&a, &b);
//引用传递
swap03(a, b);
cout << "main" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
swap03
a = 20
b = 10
main
a = 20
b = 10
常量引用
目的:修饰形参,防止误操作
做法:在函数形参列表中,可以加const修饰形参,防止形参改变实参
函数高级
函数的默认参数
1 如果某个位置参数有默认值,那么从这个位置往后,必须都要有默认值
2 声明和定义这两个,只能有一个,有默认参数,即如果函数声明有默认参数,则函数定义就不能有默认参数,不然会出现二义性
函数占位参数
- C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置;而且占位参数可以有默认参数。
- 语法: 返回值类型 函数名 (数据类型){}
void func(int a, int)
{
cout << "this is a func" << endl;
}
函数重载
目的:函数名可以相同,提高复用性
重载条件:1 同一作用域下 2 函数名称相同 3 函数参数类型不同 或者 个数不同 或者 顺序不同
注1:目前函数都是全局函数,故在全局作用域下
注2:函数的返回值不可以作为重载的条件
注意事项
- 引用作为重载条件——注意有const和无const的区别
- 函数重载遇到函数默认参数——尽量避免这种情况
#include<iostream>
using namespace std;
//引用作为重载条件
//下面两个函数可以发生重载,数据类型不同
void func(int &a)
{
cout << "func(int &a)" << endl;
}
void func(const int& a)
{
cout << "func(const int &a)" << endl;
}
//函数重载避免出现默认参数
void func2(int a)
{
cout << "func2(int a)" << endl;
}
void func2(int a, int b=10)
{
cout << "func2(int a, int b)" << endl;
}
int main()
{
int a = 10;
func(a); // int &a = a;是合法的,但int &a = 10;不合法
func(10); // const int &a = 10;是合法的
//func2(10); 存在二义性
}
func(int &a)
func(const int &a)
类和对象
C++面向对象的三大特征:封装、继承、多态
封装
目的:将属性和方法写在一起,设计类,表现事物
语法:class 类名{访问权限: 属性/方法};
访问权限
public 公共权限 —— 成员 类内and类外都可以访问
protected 保护权限 —— 成员 类内可以访问 类外不可以访问 子类可以访问父类的保护内容
private 私有权限 —— 成员 类内可以访问 类外不可以访问 ** 子类不可以访问父类的私有内容**
- protected 和 private主要在继承上有所区分,继承有父类和子类
示例1
#include<iostream>
#define PI 3.14
using namespace std;
//设计一个圆类,求周长
class Circle
{
public:
//属性
double m_R;
//方法
double cal_c()
{
return 2 * PI * m_R ;
}
};
int main()
{
//创建对象
Circle c1;
//赋值
c1.m_R = 10;
//周长
double res = c1.cal_c();
cout << "周长: " << res << endl;
}
周长: 62.8
示例2
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
void setName(string name)
{
m_Name = name;
}
void setId(int id)
{
m_id = id;
}
void printInfo()
{
cout << m_Name << endl;
cout << m_id << endl;
}
private:
string m_Name;
int m_id;
};
int main()
{
Student s;
string name = "Michael";
int id = 10;
s.setName(name);
s.setId(id);
s.printInfo();
}
Michael
10
struct和class的区别
唯一的区别在于默认的访问权限不同
struct:默认权限为公有(public)
class:默认权限为私有(private)
#include<iostream>
using namespace std;
struct S
{
int m_A; //默认公有
};
class C
{
int m_A; //默认私有
};
int main()
{
C c1;
//c1.m_A = 100; 不可访问
S s1;
s1.m_A = 100;
}
示例3——点和圆的关系(分文件的编写)
point.h
#pragma once
#include<iostream>
using namespace std;
class Point
{
public:
void set_X(int x);
int get_X();
void set_Y(int y);
int get_Y();
private:
int m_X;
int m_Y;
};
point.cpp
注意作用域,且cpp文件只需要完成方法的实现
#include "point.h"
void Point::set_X(int x)
{
m_X = x;
}
int Point::get_X()
{
return m_X;
}
void Point::set_Y(int y)
{
m_Y = y;
}
int Point::get_Y()
{
return m_Y;
}
circle.h
#pragma once
#include<iostream>
#include "point.h"
using namespace std;
class Circle
{
public:
void set_R(int r);
int get_R();
void setCenter(Point center);
Point getCenter();
void judge_Relation(Point p);
private:
Point m_Center; //圆心,可以用点类来作为另一个类的属性
int m_R;
};
circle.cpp
#include "circle.h"
void Circle::set_R(int r)
{
m_R = r;
}
int Circle::get_R()
{
return m_R;
}
void Circle::setCenter(Point center)
{
m_Center = center;
}
Point Circle::getCenter()
{
return m_Center;
}
void Circle::judge_Relation(Point p)
{
double distance = sqrt(pow(m_Center.get_X() - p.get_X(), 2) + pow(m_Center.get_Y() - p.get_Y(), 2));
if (distance > m_R)
{
cout << "点在圆外" << endl;
}
else if (distance == m_R)
{
cout << "点在圆上" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
main.cpp
#include<iostream>
#include "point.h"
#include "circle.h"
using namespace std;
int main()
{
Circle c;
c.set_R(10);
Point center;
center.set_X(10);
center.set_Y(0);
c.setCenter(center);
Point p;
p.set_X(10);
p.set_Y(10);
c.judge_Relation(p);
}
对象的初始化和清理
构造函数和析构函数
相同点:
- 没有返回值,不写void
- 在对象初始化/清理时自动调用,且只调用一次
不同点:
- 定义不同:构造函数 类名(){};析构函数 ~类名(){}
- 参数不同:构造函数可以有参数,允许函数重载;析构函数不可以
class Person
{
public:
//构造函数
Person()
{
cout << "构造函数的调用" << endl;
}
//析构函数
~Person()
{
cout << "析构函数的调用" << endl;
}
};
构造函数的几种分类
无参构造(默认)、有参构造、拷贝构造
无参构造:Person(){}
有参构造:Person(int a){}
拷贝构造:Person(const Person &p){}
注1:只要创建一个类,编译器会自动提供默认构造函数、默认拷贝构造函数(对类进行属性值拷贝)、默认析构函数
注2:若用户写了有参构造函数,则编译器不再提供默认构造函数(即此时**Person p;**这种写法是出错的),但仍然提供拷贝构造函数
注3:若用户写了拷贝构造函数,则编译器不再提供其他构造函数
#include<iostream>
using namespace std;
class Person
{
public:
//构造函数
Person()
{
cout << "Person()的无参构造函数调用" << endl;
}
Person(int a)
{
m_Age = a;
cout << "Person()的有参构造函数调用" << endl;
}
//拷贝构造函数
//const--确保本体不被修改, &--以引用的方式传递,固定写法
Person(const Person &p)
{
m_Age = p.m_Age;
cout << "Person()的拷贝构造函数调用" << endl;
}
//析构函数
~Person()
{
cout << "析构函数的调用" << endl;
}
int m_Age;
};
int main()
{
//括号法
//注意不要写成Person p1();,否则编译器会认为是函数的声明
Person p1;
Person p2(10);
Person p3(p2);
cout << "p2 age = " << p2.m_Age << endl;
cout << "p3 age = " << p3.m_Age << endl;
}
深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作,编译器默认的拷贝构造函数,容易带来内存重复释放的问题
深拷贝:在堆区重新申请空间,进行拷贝操作, 解决内存重复释放的问题
总结:若属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
浅拷贝的问题:内存重复释放
深拷贝
代码
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person的默认构造函数调用" << endl;
}
Person(int age, int height)
{
m_Age = age;
m_Height = new int(height);
cout << "Person的有参构造函数调用" << endl;
}
Person(const Person& p)
{
cout << "Person的拷贝构造函数调用" << endl;
m_Age = p.m_Age;
m_Height = p.m_Height; //浅拷贝,编译器默认实现
m_Height = new int(*p.m_Height); //深拷贝
}
~Person()
{
//析构代码,将堆区开辟数据做释放操作
if (m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
cout << "Person的析构函数调用" << endl;
}
int m_Age;
int* m_Height;
};
void test01()
{
Person p1(18, 160);
cout << "p1的年龄: " << p1.m_Age << endl;
cout << "p1的身高: " << *p1.m_Height << endl;
Person p2(p1);
cout << "p2的年龄: " << p2.m_Age << endl;
cout << "p2的身高: " << *p2.m_Height << endl;
}
int main()
{
test01();
}
初始化列表
- C++提供了初始化列表语法,用来初始化属性
- 语法:构造函数():属性1(值1),属性2(值2)… {}
- 观点:在构造函数()的基础上简化了初始化属性的操作,注意构造函数的目的就是初始化对象类对象
类成员
- C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
- A先B后,即当其他类对象作为本类成员,先构造类对象,再构造自身;析构的顺序与构造相反。
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员变量
- 所有对象共享同一份数据(因此可以通过类名访问)
- 在编译阶段分配内存(全局区),代码运行前
- 类内声明,类外初始化
#include<iostream>
using namespace std;
class Person
{
public:
//静态成员变量
static int m_A;
};
//类内声明,类外初始化
int Person::m_A = 100;
void test01()
{
//通过对象访问
Person p;
cout << p.m_A << endl;
Person p2;
p2.m_A = 200;
//静态成员变量所有对象共享同一份数据
cout << p.m_A << endl;
//通过类名访问
cout << Person::m_A << endl;
}
int main()
{
test01();
}
100
200
200
静态成员函数
- 所有对象共享同一个函数(因此可以通过类名访问)
- 静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;
class Person
{
public:
//静态成员函数
static void func()
{
m_A = 20; //可以访问
//m_B = 10; //不可访问
cout << "static void func()的调用" << endl;
}
static int m_A; //静态成员变量
int m_B; //非静态成员变量
};
int Person::m_A = 10;
void test01()
{
//通过对象访问
Person p;
p.func();
//通过类名访问
Person::func();
}
int main()
{
test01();
}
static void func()的调用
static void func()的调用
C++对象模型和this指针
成员变量和成员函数分开存储
- 在C++中,类内的成员变量和成员函数分开存储,
- 只有非静态成员变量才属于类的对象上(注意非静态成员函数、静态成员变量、静态成员函数也不属于类的对象上)——详见示例2
- 空对象占用内存空间为1,C++编译器会给空对象分配1个字节的内存空间,以区分不同对象在内存中的位置
示例1
#include<iostream>
using namespace std;
class Person
{
};
void test01()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
cout << "address of p = " << int(&p) << endl;
Person p2;
cout << "size of p2 = " << sizeof(p2) << endl;
cout << "address of p2 = " << int(&p2) << endl;
}
int main()
{
test01();
}
size of p = 1
address of p = 15727691
size of p2 = 1
address of p2 = 15727679
示例2
注意只有非静态成员变量,属于类的对象上,因此此时对象的存储空间为4!
#include<iostream>
using namespace std;
class Person
{
int m_A; //非静态成员变量,属于类的对象上
static int m_B; //静态成员变量,不属于类的对象上
void func() {} //非静态成员函数,不属于类的对象上
static void func2() {} //静态成员函数,不属于类的对象上
};
int Person::m_B = 10;
void test01()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
cout << "address of p = " << int(&p) << endl;
Person p2;
cout << "size of p2 = " << sizeof(p2) << endl;
cout << "address of p2 = " << int(&p2) << endl;
}
int main()
{
test01();
}
size of p = 4
address of p = 13629972
size of p2 = 4
address of p2 = 13629960
this指针
定义:this指针指向被调用的成员函数所属的对象,注意this是指针,且this指针的本质是指针常量,即指向无法修改
作用:1 当形参和成员变量同名时,可用this指针区分 2 在类的非静态成员函数中返回对象本身,可用return *this
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
//1 当形参和成员变量同名时,可用this指针区分
//this指针指向被调用的成员函数所属的对象
//age = age;是错误的
this->age = age;
}
//引用的方式返回本体,才能实现链式计算
Person& PersonAddAge(Person &p)
{
this->age += p.age;
//2 在类的非静态成员函数中返回对象本身,可用return *this
return *this;
}
int age;
};
void test01()
{
Person p1(18);
cout << "age of p1 = " << p1.age << endl;
}
void test02()
{
Person p1(10);
Person p2(10);
//链式编程
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
cout << "age of p2 = " << p2.age << endl;
}
int main()
{
test01();
test02();
}
age of p1 = 18
age of p2 = 50
空指针访问成员函数
- C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
- 如果用到this指针,需要加以判断保证代码的健壮性,不然会引发异常
#include<iostream>
using namespace std;
class Person
{
public:
void showClassName()
{
cout << "this is Person class" << endl;
}
void showPersonAge()
{
//报错原因为传入的指针为空指针,可进行异常处理
if (this == NULL)
{
return;
}
cout << "age = " << this->m_Age << endl;
}
int m_Age;
};
void test01()
{
//空指针
Person* p = NULL;
p->showClassName();
p->showPersonAge();
}
int main()
{
test01();
}
this is Person class
const修饰成员函数
常函数
- 成员函数后加const后,称该函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象
- 实例化对象前加const,称该对象为常对象
- 常对象只能调用常函数
#include<iostream>
using namespace std;
class Person
{
public:
//this指针的本质是指针常量,Person * const this; 即指向不能修改,但指向的数据可以修改
//const相当于也限定了this指针指向数据无法修改
//常函数
void showPerson() const
{
this->m_B = 200;
//this->m_Age = 100; const使得成员属性无法修改
//this = NULL; //this指针指向不能修改
}
void func()
{
m_Age = 10;
}
int m_Age;
mutable int m_B;
};
void test01()
{
Person p;
p.showPerson();
}
void test02()
{
const Person p; //常对象
//p.m_Age = 10; 不可修改
p.m_B = 100;
p.showPerson(); //常对象只能调用常函数
//p.func(); 不可调用,不然普通成员函数可以修改成员属性
}
int main()
{
test01();
test02();
}
友元
目的:让一个函数或者类,访问另一个类中的私有成员
关键字:friend
实现方法
- 全局函数做友元
- 类做友元
- 成员函数做友元
全局函数做友元
#include<iostream>
using namespace std;
class Building
{
//全局函数做友元,goodFriend该函数可访问Building的私有成员
friend void goodFriend(Building* b);
public:
Building()
{
m_SittingRoom = "SittingRoom";
m_BedRoom = "BedRoom";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
//全局函数
void goodFriend(Building* b)
{
cout << b->m_SittingRoom << endl;
cout << b->m_BedRoom << endl; //可以访问私有成员
}
void test01()
{
Building b;
goodFriend(&b);
}
int main()
{
test01();
}
SittingRoom
BedRoom
类做友元
#include<iostream>
using namespace std;
class Building
{
///类做友元,GoodFriend该类可访问Building的私有成员
friend class GoodFriend;
public:
Building()
{
m_SittingRoom = "SittingRoom";
m_BedRoom = "BedRoom";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
class GoodFriend
{
public:
GoodFriend()
{
//留意下写法
b = new Building;
}
void visit()
{
cout << b->m_SittingRoom << endl;
cout << b->m_BedRoom << endl; //类做友元,可以访问私有属性
}
Building* b;
};
void test01()
{
GoodFriend g;
g.visit();
}
int main()
{
test01();
}
SittingRoom
BedRoom
成员函数做友元
#include<iostream>
using namespace std;
class Building;
class GoodFriend
{
public:
GoodFriend();
//让成员函数能够访问Building中的私有成员
void visit();
Building* b;
};
class Building
{
//成员函数做友元,支持
friend void GoodFriend::visit();
public:
Building()
{
m_SittingRoom = "SittingRoom";
m_BedRoom = "BedRoom";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
//注意下面两个GoodFriend函数必须在下面再定义,因为用到了Building类,注意顺序
GoodFriend::GoodFriend()
{
b = new Building;
}
void GoodFriend::visit()
{
cout << b->m_SittingRoom << endl;
cout << b->m_BedRoom << endl;
}
void test01()
{
GoodFriend g;
g.visit();
}
int main()
{
test01();
}
SittingRoom
BedRoom
运算符重载
目的:实现对自定义的数据类型,进行加减乘除等操作
加号运算符重载operator+
1 注意成员函数和全局函数调用时的本质区别
2 运算符重载也可以进行函数重载
#include<iostream>
using namespace std;
class Person
{
public:
//通过成员函数进行加号运算符重载
Person operator+ (Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A;
int m_B;
};
//通过全局函数进行加号运算符重载
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载
Person operator+(Person& p1, int a)
{
Person temp;
temp.m_A = p1.m_A + a;
temp.m_B = p1.m_B + a;
return temp;
}
void test01()
{
Person p1;
p1.m_A = 10;
p1.m_B = 20;
Person p2;
p2.m_A = 30;
p2.m_B = 40;
//注意二者本质的区别
//成员函数重载的本质调用: Person p3 = p1.operator+(p2);
//全局函数重载的本质调用: Person p3 = operator+(p1, p2);
Person p3 = p1 + p2;
cout << "p3 m_A = " << p3.m_A << endl;
cout << "p3 m_B = " << p3.m_B << endl;
//函数重载
Person p4 = p1 + 10;
cout << "p4 m_A = " << p4.m_A << endl;
cout << "p4 m_B = " << p4.m_B << endl;
}
int main()
{
test01();
}
p3 m_A = 40
p3 m_B = 60
p4 m_A = 20
p4 m_B = 30
左移运算符重载operator<<
作用:可以输出自定义数据类型;重载左移运算符配合友元可以实现输出自定义数据类型
注意1:一般不用成员函数重载左移运算符
注意2:cout属于ostream数据类型
#include<iostream>
using namespace std;
class Person
{
public:
Person(int a, int b)
{
m_A = a;
m_B = b;
}
friend ostream& operator<<(ostream& cout, Person& p);
private:
int m_A;
int m_B;
};
//全局函数重载左移运算符
//本质: operator<<(cout, p) --> cout << p
ostream& operator<<(ostream &cout, Person& p)
{
cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
return cout;
}
void test01()
{
Person p(10, 10);
//本质: operator<<(cout, p);
//链式思想
cout << p << endl;
}
int main()
{
test01();
}
m_A = 10 m_B = 10
递增运算符重载operator++
作用: 通过重载递增运算符,实现自己的整型数据
总结: 前置递增返回引用(链式法则,实现不断递增),后置递增返回值(局部变量,调用完销毁)
#include<iostream>
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 10;
}
//重载++运算符(前置)++a
//返回引用的目的是一直对一个数据进行递增操作,链式法则
MyInteger& operator++()
{
//先+1,再返回
m_Num += 1;
return *this;
}
//重载++运算符(后置)a++
//int为占位符,区分前置和后置递增
//这里返回值,因为若返回引用,由于函数体内temp是局部变量,因此函数运行完会销毁,出错
MyInteger operator++(int)
{
//先记录结果,再+1,再返回此前记录的结果
MyInteger temp = *this;
m_Num += 1;
return temp;
}
private:
int m_Num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_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();
}
10
11
赋值运算符重载operator=
防止浅拷贝带来的内存重复释放的问题
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
m_Age = new int(age);
}
//重载赋值运算符=
Person& operator=(Person& p)
{
//先判断是否有堆区内存数据
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//然后进行深拷贝
m_Age = new int(*p.m_Age);
return *this;
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
int *m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
cout << "p1 age = " << * p1.m_Age << endl;
cout << "p2 age = " << *p2.m_Age << endl;
p2 = p1; //赋值操作,重载,由浅拷贝变为深拷贝
cout << "p1 age = " << *p1.m_Age << endl;
cout << "p2 age = " << *p2.m_Age << endl;
Person p3(10);
p3 = p2 = p1; //链式思想
cout << "p1 age = " << *p1.m_Age << endl;
cout << "p2 age = " << *p2.m_Age << endl;
cout << "p3 age = " << *p3.m_Age << endl;
}
int main()
{
test01();
}
p1 age = 18
p2 age = 20
p1 age = 18
p2 age = 18
p1 age = 18
p2 age = 18
p3 age = 18
关系运算符重载
#include<iostream>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
//重载==号
bool operator==(Person& p)
{
if ((m_Name == p.m_Name) and (m_Age == p.m_Age))
{
return true;
}
else
{
return false;
}
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
if (p1 == p2)
{
cout << "p1 = p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
int main()
{
test01();
}
p1 = p2
函数调用运算符重载——仿函数
1 函数调用运算符**()**进行重载
#include<iostream>
using namespace std;
class MyPrint
{
public:
//重载函数调用运算符(),跟重载[]一样,可以通过a[i]访问
void operator()(string s)
{
cout << s << endl;
}
};
class MyAdd
{
public:
int operator()(int a, int b)
{
int res = a + b;
return res;
}
};
void test01()
{
MyPrint myprint;
myprint("Hello World");
}
void test02()
{
MyAdd myadd;
int res = myadd(10, 10);
cout << res << endl;
}
int main()
{
test01();
test02();
}
Hello World
20
继承
目的:提炼共性,减少重复代码
语法:在定义类时使用冒号加以继承,即class 子类 : 继承方式 父类
,比如class Java : public BasePage
注:子类派生类,父类基类
继承方式
公有继承、保护继承、私有继承
1 父类中的私有内容,子类均访问不到
2 公有继承: 保持不变,保护、私有继承:跟随变化
#include<iostream>
using namespace std;
//父类
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公有继承
class Son1 :public Base
{
public:
void func()
{
m_A = 10; //变为公共权限
m_B = 20; //变为保护权限
//m_C = 30; //私有属性不可访问
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;
//s1.m_B = 200; // 类外, 保护权限不可访问
}
//保护继承
class Son2 :protected Base
{
public:
void func()
{
m_A = 10; //变为保护权限
m_B = 20; //变为保护权限
//m_C = 30; //私有属性不可访问
}
};
void test02()
{
Son2 s2;
//s2.m_A = 100; // 类外, 保护权限不可访问
//s2.m_B = 200; // 类外, 保护权限不可访问
}
//私有继承
class Son3 :private Base
{
public:
void func()
{
m_A = 10; //变为私有权限
m_B = 20; //变为私有权限
//m_C = 30; //私有属性不可访问
}
};
void test03()
{
Son3 s3;
//s3.m_A = 100; // 类外, 私有权限不可访问
//s3.m_B = 200; // 类外, 私有权限不可访问
}
int main()
{
}
继承中的对象模型
思想:父类中所有非静态成员属性都会被子类继承下去
查询指令:命令行
cl /d1 reportSingleClassLayoutSon 129-类和对象-继承-继承中的对象模型.cpp
#include<iostream>
using namespace std;
//父类
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公有继承
class Son1 :public Base
{
public:
int m_D;
};
void test01()
{
Son1 s1;
cout << "sizeof(s1) = " << sizeof(s1) << endl;
}
int main()
{
test01();
}
sizeof(s1) = 16
继承中的构造和析构顺序
构造顺序:先构造父类,再构造子类
析构顺序:先析构子类,再析构父类
#include<iostream>
using namespace std;
//父类
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();
}
Base构造函数
Son构造函数
Son析构函数
Base析构函数
继承同名成员处理方式
1 访问子类同名成员,直接访问
2 访问父类同名成员,需要加作用域
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
m_A = 100;
}
void func()
{
cout << "Base func()" << endl;
}
void func(int a)
{
cout << "Base func(int a)" << endl;
}
int m_A;
};
class Son :public Base
{
public:
Son()
{
m_A = 200;
}
void func()
{
cout << "Son func()" << endl;
}
int m_A;
};
//同名成员属性处理方式
void test01()
{
Son s;
cout << "Base m_A = " << s.Base::m_A << endl;
cout << "Son m_A = " << s.m_A << endl;
}
//同名成员函数处理方式
void test02()
{
Son s;
s.Base::func();
s.func();
s.Base::func(10);
}
int main()
{
test01();
test02();
}
Base m_A = 100
Son m_A = 200
Base func()
Son func()
Base func(int a)
继承同名静态成员处理方式
1 访问子类同名成员,直接访问
2 访问父类同名成员,需要加作用域
3 注意静态成员有两种访问方式,通过对象and通过类名
#include<iostream>
using namespace std;
class Base
{
public:
static void func()
{
cout << "Base func()" << endl;
}
static void func(int a)
{
cout << "Base func(int a)" << endl;
}
static int m_A;
};
int Base::m_A = 100;
class Son :public Base
{
public:
static void func()
{
cout << "Son func()" << endl;
}
static int m_A;
};
int Son::m_A = 200;
//同名静态成员属性处理方式
void test01()
{
//通过对象访问
Son s;
cout << "Base m_A = " << s.Base::m_A << endl;
cout << "Son m_A = " << s.m_A << endl;
//通过类名访问
//Son::Base::m_A和Base::m_A是一样的
cout << "Base m_A = " << Son::Base::m_A << endl;
cout << "Son m_A = " << Son::m_A << endl;
}
//同名成员函数处理方式
void test02()
{
//通过对象访问
Son s;
s.Base::func();
s.func();
s.Base::func(10);
//通过类名访问
Son::Base::func();
Son::func();
Son::Base::func(10);
}
int main()
{
test01();
test02();
}
Base m_A = 100
Son m_A = 200
Base m_A = 100
Son m_A = 200
Base func()
Son func()
Base func(int a)
Base func()
Son func()
Base func(int a)
多继承语法
语法:class 子类 : 继承方式 父类1, 继承方式 父类2 ...
注:遇到同名成员时,需要加作用域区分,一般不建议使用
#include<iostream>
using namespace std;
class Base1
{
public:
Base1()
{
m_A = 100;
}
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 200;
}
int m_A;
};
class Son : public Base1, public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01()
{
Son s;
cout << "sizeof(s) = " << sizeof(s) << endl;
//需要加作用域
cout << "Base1 m_A = " << s.Base1::m_A << endl;
cout << "Base2 m_A = " << s.Base2::m_A << endl;
}
int main()
{
test01();
}
sizeof(s) = 16
Base1 m_A = 100
Base2 m_A = 200
菱形继承
1 当菱形继承时,两个父类拥有同名数据,可以用作用域加以区分
2 菱形继承导致数据有两份,存在二义性,且造成资源浪费,可以用虚继承加以解决
3 虚继承的目的:将数据变为1份,且加以共享;Base作为虚基类
#include<iostream>
using namespace std;
class Animal
{
public:
int m_Age;
};
//虚继承,Animal作为虚基类
class Sheep :virtual public Animal
{
};
class Camel :virtual public Animal
{
};
class SheepCamel :public Sheep, public Camel
{
};
void test01()
{
SheepCamel sc;
sc.Sheep::m_Age = 18;
sc.Camel::m_Age = 28;
//当菱形继承时,两个父类拥有同名数据,可以用作用域加以区分
cout << "sc Sheep::m_Age = " << sc.Sheep::m_Age << endl;
cout << "sc Camel::m_Age = " << sc.Camel::m_Age << endl;
//使用虚继承后,本质上就是将同名变量,共享同一份数据内存
cout << "sc m_Age = " << sc.m_Age << endl;
}
int main()
{
test01();
}
sc Sheep::m_Age = 28
sc Camel::m_Age = 28
sc m_Age = 28
多态
基本知识
静态多态:函数重载、运算符重载,复用函数名
动态多态:派生类、虚函数
区别:静态多态函数地址早绑定(编译阶段确定函数地址)、动态多态函数地址晚绑定(运行阶段确定函数地址)
动态多态的条件
1 有继承关系
2 子类重写父类的虚函数
3 使用:父类的指针或者引用,指向子类对象
多态的原理
1 通过在父类的函数添加virtual关键字,实现多态,会形成指针vfptr——虚函数(表)指针,指向vftable——虚函数表,记录函数的入口地址
2 当子类重写父类的虚函数时,子类中的虚函数表内部会被替换成子类的虚函数地址
3 注意指针占4个字节,与数据类型无关
多态的优点
1 代码组织结构清晰
2 可读性强
3 利于前期和后期的扩展及维护
注:在开发中,提倡开闭原则,即对扩展进行开放,对修改进行关闭
#include<iostream>
using namespace std;
class Animal
{
public:
//虚函数——动态多态函数,地址晚绑定
virtual void speak()
{
cout << "Animal speak" << endl;
}
};
//1. 继承关系
class Cat : public Animal
{
public:
//2. 子类重写父类的虚函数, virtual可写可不写
void speak()
{
cout << "Cat speak" << endl;
}
};
//1. 继承关系
class Dog : public Animal
{
public:
//2. 子类重写父类的虚函数, virtual可写可不写
void speak()
{
cout << "Dog speak" << endl;
}
};
//3. 父类的指针或者引用,指向子类对象
void doSpeak1(Animal& animal) //Animal& animal = cat;
{
animal.speak();
}
void doSpeak2 (Animal* animal) //Animal* animal = &cat;
{
animal->speak();
}
void test01()
{
Cat cat;
doSpeak1(cat);
doSpeak2(&cat);
Dog dog;
doSpeak1(dog);
doSpeak2(&dog);
}
void test02()
{
Animal animal;
cout << "sizeof(animal) = " << sizeof(animal) << endl;
}
int main()
{
test01();
test02();
}
Cat speak
Cat speak
Dog speak
Dog speak
sizeof(animal) = 4
案例-多态实现计算器
1 组织结构清晰 2 可读性强 3 易扩展
#include<iostream>
using namespace std;
class Calculator
{
public:
int getResult(string oper)
{
if (oper == "+")
{
return m_Num1 + m_Num2;
}
else if (oper == "-")
{
return m_Num1 - m_Num2;
}
else if (oper == "*")
{
return m_Num1 * m_Num2;
}
}
int m_Num1;
int m_Num2;
};
void test01()
{
Calculator c1;
c1.m_Num1 = 10;
c1.m_Num2 = 10;
cout << c1.m_Num1 << " + " << c1.m_Num2 << " = " << c1.getResult("+") << endl;
cout << c1.m_Num1 << " - " << c1.m_Num2 << " = " << c1.getResult("-") << endl;
cout << c1.m_Num1 << " * " << c1.m_Num2 << " = " << c1.getResult("*") << endl;
}
//利用多态实现计算器,采用开闭原则的思想
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
class AddCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 + m_Num2;
}
};
class MinusCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 - m_Num2;
}
};
class MulCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 * m_Num2;
}
};
void test02()
{
//父类指针或者引用指向子类对象
AbstractCalculator* abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
//加法运算
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
//堆区开放,需要销毁
delete abc;
//减法运算
abc = new MinusCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
//堆区开放,需要销毁
delete abc;
//乘法运算
abc = new MulCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
//堆区开放,需要销毁
delete abc;
}
int main()
{
test01();
test02();
}
10 + 10 = 20
10 - 10 = 0
10 * 10 = 100
10 + 10 = 20
10 - 10 = 0
10 * 10 = 100
纯虚函数和抽象类
纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;
当类中只要有纯虚函数,这个类就是抽象类
注1:抽象类无法实例化对象
注2:子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include<iostream>
using namespace std;
//抽象类
class Base
{
public:
//纯虚函数
virtual void func() = 0;
};
class Son :public Base
{
public:
//重写抽象类中的纯虚函数
void func()
{
cout << "Son func函数调用" << endl;
}
};
void test01()
{
Base* b = new Son;
b->func();
}
int main()
{
test01();
}
Son func函数调用
案例-制作饮品
多态:一个接口,根据不同的对象执行对应的代码
#include<iostream>
using namespace std;
class AbstractDrinking
{
public:
//煮水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加入辅料
virtual void PutSomething() = 0;
void makeDrink()
{
Boil();
Brew();
PourInCup();
PutSomething();
}
};
//制作咖啡
class Coffee :public AbstractDrinking
{
//煮水
virtual void Boil()
{
cout << "Coffee Boil" << endl;
}
//冲泡
virtual void Brew()
{
cout << "Coffee Brew" << endl;
}
//倒入杯中
virtual void PourInCup()
{
cout << "Coffee PourInCup" << endl;
}
//加入辅料
virtual void PutSomething()
{
cout << "Coffee PutSomething" << endl;
}
};
//制作茶
class Tea :public AbstractDrinking
{
//煮水
virtual void Boil()
{
cout << "Tea Boil" << endl;
}
//冲泡
virtual void Brew()
{
cout << "Tea Brew" << endl;
}
//倒入杯中
virtual void PourInCup()
{
cout << "Tea PourInCup" << endl;
}
//加入辅料
virtual void PutSomething()
{
cout << "Tea PutSomething" << endl;
}
};
void test01()
{
AbstractDrinking* a = new Coffee;
a->makeDrink();
delete a;
cout << "------------" << endl;
a = new Tea;
a->makeDrink();
}
int main()
{
test01();
}
Coffee Boil
Coffee Brew
Coffee PourInCup
Coffee PutSomething
------------
Tea Boil
Tea Brew
Tea PourInCup
Tea PutSomething
虚析构和纯虚析构
背景:使用多态时,若子类中有属性开辟到堆区,则父类指针在释放时无法调用到子类的析构代码
解决方法:将父类的析构函数改为虚析构或者纯虚析构
两者的共性
1 都可以实现父类指针释放子类对象
2 都需要具体的函数实现
两者的不同
若是纯虚析构,则该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0;
类名::~类名(){}
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal 构造" << endl;
}
virtual void speak() = 0;
//虚析构,实现对Cat中析构函数的调用,并释放子类对象
virtual ~Animal()
{
cout << "Animal 析构" << endl;
}
};
class Cat :public Animal
{
public:
Cat(string name)
{
cout << "Cat 构造" << endl;
m_Name = new string(name);
}
void speak()
{
cout << "Cat" << " " << *m_Name << " speak" << endl;
}
~Cat()
{
if (m_Name != NULL)
{
cout << "Cat 析构" << endl;
delete m_Name;
m_Name = NULL;
}
}
string *m_Name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main()
{
test01();
}
Animal 构造
Cat 构造
Cat Tom speak
Cat 析构
Animal 析构
文件操作
目的:通过文件将数据持久化
做法:头文件
文件类型
1 文本文件:以ASCII码进行存储
2 二进制文件:以二进制形式进行存储
操作文件的三大类
ofstream:写操作(output)
ifstream:读操作(input)
fstream:读写操作
文件打开方式
文件打开方式可以配合使用,利用 | 操作符,如以二进制方式写文件ios::binary | ios::out
写文件程序
#include<iostream>
//1 包含头文件
#include<fstream>
using namespace std;
int main()
{
//2 创建流对象
ofstream ofs;
//3 打开文件, 写操作
ofs.open("./file.txt", ios::out);
//4 写数据
ofs << "Hello World!";
//5 关闭文件
ofs.close();
}
读文件程序
注1:is_open()可以判断打开文件是否成功
注2:使用第三种读取数据比较好
//第三种
string buffer;
while (getline(ifs, buffer))
{
cout << buffer << endl;
}
完整代码
#include<iostream>
#include<string>
//1 包含头文件
#include<fstream>
using namespace std;
int main()
{
//2 创建流对象
ifstream ifs;
//3 打开文件, 读操作
ifs.open("./file.txt", ios::in);
//判断文件是否打开成功
if (!ifs.is_open())
{
cout << "文件打开失败!" << endl;
return 0;
}
//4 读数据
第一种
//char buffer[1024] = { 0 };
//while (ifs >> buffer)
//{
// cout << buffer << endl;
//}
第二种
//char buffer[1024] = { 0 };
//while (ifs.getline(buffer, sizeof(buffer)))
//{
// cout << buffer << endl;
//}
//第三种
string buffer;
while (getline(ifs, buffer))
{
cout << buffer << endl;
}
第四种
//char c;
//while ((c = ifs.get()) != EOF) // EOF end of file
//{
// cout << c;
//}
//5 关闭文件
ifs.close();
}
二进制写文件程序
1 使用write进行写文件
2 函数原型:ostream& write(const char * buffer, int len);
参数解释:字符指针buffer指向内存中一段存储空间,len时读写的字节数
ofs.write((const char*)&p, sizeof(Person));
#include<iostream>
#include<string>
//1 包含头文件
#include<fstream>
using namespace std;
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01()
{
//2 创建流对象
ofstream ofs;
//3 打开文件, 读操作
ofs.open("person.txt", ios::out | ios::binary);
//4 写文件
Person p = { "Tom", 18 };
ofs.write((const char*)&p, sizeof(Person));
//5 关闭文件
ofs.close();
}
int main()
{
test01();
}
二进制读文件程序
ifs.read((char*)&p, sizeof(Person));
#include<iostream>
#include<string>
//1 包含头文件
#include<fstream>
using namespace std;
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01()
{
//2 创建流对象
ifstream ifs;
//3 打开文件, 读操作
ifs.open("person.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//4 读文件
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << "p name = " << p.m_Name << " age = " << p.m_Age << endl;
//5 关闭文件
ifs.close();
}
int main()
{
test01();
}
职工管理系统
关键在于思维的锻炼,从面向过程到面向对象
main.cpp
#include<iostream>
#include "workerManager.h"
using namespace std;
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;
}
}
system("pause");
return 0;
}
workerManager.h
#pragma once
#include<iostream>
#include<fstream>
#include "worker.h"
#include "employee.h"
#include "manager.h"
#include "boss.h"
#define FILENAME "empfile.txt"
using namespace std;
class WorkerManager
{
public:
//构造函数
WorkerManager();
//析构函数
~WorkerManager();
//case0 退出系统
void exitSystem();
//case1 添加职工
void add_Emp();
//case2 显示职工
void show_Emp();
//case3 删除职工
void del_Emp();
//case4 修改职工
void mod_Emp();
//case5 查找职工
void find_Emp();
//case6 职工排序
void sort_Emp();
//case7 清空文件
void clean_File();
//其他功能函数
//展示菜单
void show_Menu();
//初始化职工,将文件中的数据存储到数组中
void init_EMp();
//判断职工是否存在,若存在,返回位置索引,否则返回-1
int isExist(int id);
//写入文件并加以保存
void save();
//统计人数
int get_EmpNum();
//成员属性
//记录职工人数
int m_EmpNum;
//职工数组指针
Worker** m_EmpArray;
//标志文件是否为空
bool m_FileIsEmpty;
};
workerManager.cpp
#include "workerManager.h"
//构造函数
WorkerManager::WorkerManager()
{
ifstream ifs;
ifs.open(FILENAME, ios::in);
//情况1. 文件不存在
if (!ifs.is_open())
{
//初始化属性
//初始化记录人数
this->m_EmpNum = 0;
//初始化数组指针
this->m_EmpArray = NULL;
//初始化文件是否为空
this->m_FileIsEmpty = true;
ifs.close();
return;
}
//情况2. 文件存在但无数据
char ch;
ifs >> ch;
//表示文件为空
if (ifs.eof())
{
//初始化记录人数
this->m_EmpNum = 0;
//初始化数组指针
this->m_EmpArray = NULL;
//初始化文件是否为空
this->m_FileIsEmpty = true;
ifs.close();
return;
}
//情况3. 文件存在且有数据
int num = this->get_EmpNum();
this->m_EmpNum = num;
//开辟空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将文件中的数据存储到数组中
this->init_EMp();
}
//析构函数
WorkerManager::~WorkerManager()
{
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i] != NULL)
{
delete this->m_EmpArray[i];
}
}
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
}
}
//case0 退出系统
void WorkerManager::exitSystem()
{
cout << "欢迎下次使用" << endl;
exit(0); //退出程序
}
//case1 添加职工
void WorkerManager::add_Emp()
{
cout << "请输入添加职工数量: " << endl;
int addNum = 0;
cin >> addNum;
if (addNum > 0)
{
//新空间大小
int newSize = this->m_EmpNum + addNum;
//开辟新空间
Worker** newSpace = new Worker * [newSize];
//将原来空间下数据,拷贝到新空间下
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
newSpace[i] = this->m_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[i + this->m_EmpNum] = worker;
}
//释放原有空间
delete[] this->m_EmpArray;
//更改新空间的指向
this->m_EmpArray = newSpace;
//更新新职工人数
this->m_EmpNum = newSize;
//更新职工不为空的标志
this->m_FileIsEmpty = false;
//提示添加成功
cout << "成功添加" << addNum << "名新职工" << endl;
//保存到文件里
this->save();
}
else
{
cout << "输入有误" << endl;
}
//按任意键后,清屏返回上级目录
system("pause");
system("cls");
}
//case2 显示职工
void WorkerManager::show_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空" << endl;
}
else
{
for (int i = 0; i < this->m_EmpNum; i++)
{
//利用多态调用程序接口
this->m_EmpArray[i]->showInfo();
}
}
system("pause");
system("cls");
}
//case3 删除职工
void WorkerManager::del_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空" << endl;
}
else
{
//按照职工编号来删除
cout << "请输入想要删除的职工编号: " << endl;
int id;
cin >> id;
int index = this->isExist(id);
//职工存在
if (index != -1)
{
for (int i = index; i < this->m_EmpNum - 1; i++)
{
//数组前移
this->m_EmpArray[i] = this->m_EmpArray[i + 1];
}
//更新数组成员个数
this->m_EmpNum -= 1;
//数据更新到文件
this->save();
cout << "删除成功! " << endl;
}
else
{
cout << "找不到该职工" << endl;
}
}
system("pause");
system("cls");
}
//case4 修改职工
void WorkerManager::mod_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空" << endl;
}
else
{
//按照职工编号来修改
cout << "请输入想要修改的职工编号: " << endl;
int id;
cin >> id;
int index = this->isExist(id);
//职工存在
if (index != -1)
{
delete this->m_EmpArray[index];
int newId;
string newName;
int newdId;
cout << "查到" << id << "号职工" << endl;
cout << "请输入新的职工号: " << endl;
cin >> newId;
cout << "请输入新的姓名: " << endl;
cin >> newName;
cout << "请输入新的岗位: " << endl;
cout << "1. 普通职工" << endl;
cout << "2. 经理" << endl;
cout << "3. 老板" << endl;
cin >> newdId;
Worker* worker = NULL;
switch (newdId)
{
case 1:
worker = new Employee(newId, newName, newdId);
break;
case 2:
worker = new Manager(newId, newName, newdId);
break;
case 3:
worker = new Boss(newId, newName, newdId);
break;
}
//更新数据到数组中
this->m_EmpArray[index] = worker;
cout << "修改成功" << endl;
//保存文件
this->save();
}
else
{
cout << "找不到该职工" << endl;
}
}
system("pause");
system("cls");
}
//case5 查找职工
void WorkerManager::find_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空" << endl;
}
else
{
cout << "请输入查找的方式: " << endl;
cout << "1. 按照职工编号查找" << endl;
cout << "2. 按照职工姓名查找" << endl;
int select;
cin >> select;
if (select == 1)
{
//按照编号查找
int id;
cout << "请输入查找的职工编号: " << endl;
cin >> id;
int index = isExist(id);
if (index != -1)
{
cout << "查找成功" << endl;
this->m_EmpArray[index]->showInfo();
}
else
{
cout << "查找失败,查无此人" << endl;
}
}
else if (select == 2)
{
//按照姓名查找
string name;
cout << "请输入查找的职工姓名: " << endl;
cin >> name;
bool flag = false;
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Name == name)
{
cout << "查找成功, 职工编号为: "
<< this->m_EmpArray[i]->m_Id << "号的职工信息如下: " << endl;
flag = true;
this->m_EmpArray[i]->showInfo();
}
}
if (flag == false)
{
cout << "查找失败, 查无此人" << endl;
}
}
else
{
cout << "输入有误" << endl;
}
}
system("pause");
system("cls");
}
//case6 职工排序
void WorkerManager::sort_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或者记录为空" << endl;
}
else
{
cout << "请选择排序方式" << endl;
cout << "1. 按照职工号进行升序" << endl;
cout << "2. 按照职工号进行降序" << endl;
int select;
cin >> select;
if (select == 1)
{
for (int i = 0; i < this->m_EmpNum - 1; i++)
{
for (int j = 0; j < this->m_EmpNum - i - 1; j++)
{
if (this->m_EmpArray[j]->m_Id > this->m_EmpArray[j + 1]->m_Id)
{
Worker* worker = this->m_EmpArray[j];
this->m_EmpArray[j] = this->m_EmpArray[j + 1];
this->m_EmpArray[j + 1] = worker;
}
}
}
cout << "升序排序成功" << endl;
}
else if (select == 2)
{
for (int i = 0; i < this->m_EmpNum - 1; i++)
{
for (int j = 0; j < this->m_EmpNum - i - 1; j++)
{
if (this->m_EmpArray[j]->m_Id < this->m_EmpArray[j + 1]->m_Id)
{
Worker* worker = this->m_EmpArray[j];
this->m_EmpArray[j] = this->m_EmpArray[j + 1];
this->m_EmpArray[j + 1] = worker;
}
}
}
cout << "降序排序成功" << endl;
}
else
{
cout << "输入有误" << endl;
}
//更新文件
this->save();
}
system("pause");
system("cls");
}
//case7 清空文件
void WorkerManager::clean_File()
{
cout << "确定清空吗?" << endl;
cout << "1. 确定" << endl;
cout << "2. 返回" << endl;
int select;
cin >> select;
if (select == 1)
{
//清空文件
ofstream ofs;
ofs.open(FILENAME, ios::trunc);
ofs.close();
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
delete this->m_EmpArray[i];
this->m_EmpArray[i] = NULL;
}
//删除堆区数组指针
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
}
cout << "清空成功" << endl;
}
system("pause");
system("cls");
}
//其他功能函数
//展示菜单
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::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;
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->m_EmpArray[index] = worker;
index += 1;
}
ifs.close();
}
//判断职工是否存在,若存在,返回位置索引,否则返回-1
int WorkerManager::isExist(int id)
{
int index = -1;
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Id == id)
{
index = i;
break;
}
}
return index;
}
//写入文件并加以保存
void WorkerManager::save()
{
ofstream ofs;
ofs.open(FILENAME, ios::out);
for (int i = 0; i < m_EmpNum; i++)
{
ofs << m_EmpArray[i]->m_Id << " "
<< m_EmpArray[i]->m_Name << " "
<< m_EmpArray[i]->m_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 += 1;
}
return num;
}
worker.h
#pragma once
#include<iostream>
using namespace std;
class Worker
{
public:
//显示个人信息
virtual void showInfo() = 0;
//获取岗位名称
virtual string getDeptName() = 0;
int m_Id; //职工编号
string m_Name; //职工姓名
int m_DeptId; //职工所在部分编号
};
employee.h
#pragma once
#include<iostream>
#include "worker.h"
using namespace std;
//普通员工
class Employee: public Worker
{
public:
//构造函数
Employee(int id, string name, int dId);
//显示个人信息
void showInfo();
//获取岗位名称
string getDeptName();
};
employee.cpp
#include "employee.h"
Employee::Employee(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}
void Employee::showInfo()
{
cout << "职工编号: " << this->m_Id
<< "\t职工姓名: " << this->m_Name
<< "\t岗位: " << this->getDeptName()
<< "\t职责: 完成经理交给的任务" << endl;
}
string Employee::getDeptName()
{
return "员工";
}
manager.h
#pragma once
#include<iostream>
#include "worker.h"
class Manager : public Worker
{
public:
//构造函数
Manager(int id, string name, int dId);
//显示个人信息
void showInfo();
//获取岗位名称
string getDeptName();
};
manager.cpp
#include "manager.h"
Manager::Manager(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}
void Manager::showInfo()
{
cout << "职工编号: " << this->m_Id
<< "\t职工姓名: " << this->m_Name
<< "\t岗位: " << this->getDeptName()
<< "\t职责: 完成老板任务,并下发给员工" << endl;
}
string Manager::getDeptName()
{
return "经理";
}
boss.h
#pragma once
#include<iostream>
#include "worker.h"
class Boss : public Worker
{
public:
//构造函数
Boss(int id, string name, int dId);
//显示个人信息
void showInfo();
//获取岗位名称
string getDeptName();
};
boss.cpp
#include "boss.h"
Boss::Boss(int id, string name, int dId)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}
void Boss::showInfo()
{
cout << "职工编号: " << this->m_Id
<< "\t职工姓名: " << this->m_Name
<< "\t岗位: " << this->getDeptName()
<< "\t职责: 管理公司所有事务" << endl;
}
string Boss::getDeptName()
{
return "老板";
}