C++学习三部曲(核心编程)

本文深入探讨了C++编程中的内存管理,包括内存分区(代码区、全局区、栈区、堆区),以及对象的生命周期。接着讲解了类和对象的概念,包括构造函数、析构函数、成员函数、静态成员、友元等。此外,还介绍了继承和多态,如虚函数、纯虚函数、抽象类,以及动态多态实现。最后,讨论了文件操作和动态多态在职工管理系统中的应用,展示了C++在实际问题解决中的能力。
摘要由CSDN通过智能技术生成

内存分区模型

  • 运行前的区域:代码区、全局区
  • 运行后的区域:栈区、堆区

代码区

存放函数体的二进制代码,特点是共享只读

全局区

存放全局变量、静态变量(在普通变量前面加上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

image.png

栈区

由编译器自动分配释放,存放函数的参数值、局部变量等,栈区的数据在函数执行完后自动释放

堆区

由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收,利用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 声明和定义这两个,只能有一个,有默认参数,即如果函数声明有默认参数,则函数定义就不能有默认参数,不然会出现二义性
image.png

函数占位参数

  • C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置;而且占位参数可以有默认参数
  • 语法: 返回值类型 函数名 (数据类型){}
void func(int a, int)
{
	cout << "this is a func" << endl;
}

函数重载

目的:函数名可以相同,提高复用性
重载条件:1 同一作用域下 2 函数名称相同 3 函数参数类型不同 或者 个数不同 或者 顺序不同
注1:目前函数都是全局函数,故在全局作用域下
注2:函数的返回值不可以作为重载的条件
image.png

注意事项

  • 引用作为重载条件——注意有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)
image.png

#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;
}
深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作,编译器默认的拷贝构造函数,容易带来内存重复释放的问题
深拷贝:在堆区重新申请空间,进行拷贝操作, 解决内存重复释放的问题
总结:若属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
浅拷贝的问题:内存重复释放
image.png
深拷贝
image.png

代码
#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后,即当其他类对象作为本类成员,先构造类对象,再构造自身;析构的顺序与构造相反。

image.png

静态成员

静态成员就是在成员变量和成员函数前加上关键字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 公有继承: 保持不变,保护、私有继承:跟随变化
image.png

#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

image.png

#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作为虚基类
image.png

#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 利于前期和后期的扩展及维护
注:在开发中,提倡开闭原则,即对扩展进行开放,对修改进行关闭
image.png
image.png
image.png

#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
image.png

写文件程序

#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();
}

职工管理系统

关键在于思维的锻炼,从面向过程到面向对象
image.png
image.png

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 "老板";
}

参考链接

黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难(P84-P166)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值