c++笔记

我自己的命名规则

类名:首字母大写其他小写 Circle

函数名:驼峰 calculateCircle()

变量名 :全部小写,用下划线链接 circle_r

使用技巧

ctrl + k 然后 ctrl + c 多行注释

ctrl + k 然后 ctrl + u 取消注释

一个项目中只可以有一个main()

true、false是小写的

cout输出

cin输入 cin>>

运算符:

​ ±*/

​ %取模,10%3=1

​ ++a a++

​ --a a–

比较运算符:

​ 返回的是一个布尔值

逻辑运算:

! &&(与) ||(或)

类定义的{}后面但是函数的函数体后面

遗漏的知识点

数组名做函数形参,实参会改变

在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢?在我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。因此当形参数组发生变化时,实参数组也随之变化。

return

函数一旦return就会返回,后面的一切都不会在执行

int = 字母

cin>>值;

如果用户输入的不是数字而是字母的话,int也会被赋值,会变成一个极大无比的数!并存在内存里面

static

当static关键字被用来定义类中的方法和变量时,所有的实例都共享这一块内存,即在所有的实例中,所有的这个变量都是同一个
当ststic关键被用来定义函数时,表示这个函数仅对这个cpp文件可见
当ststic关键字用来定义函数里面的变量时,表示这个变量只在第一次定义的时候初始化,后面再运行时不会重新初始化!

随机数生成

rand()%100 生成一个0~99的随机数

一般rand要给种子,#include<time.h>

long t=time(0);

srand(t); 其中t就是种子

对于一个函数而言只需要srand(t)一次,之后rand()生成的随机数就不一样了

time(0)是long类型, 其值是系统从1970年1月1日00:00:00到现在总共的秒数

各种语句

顺序、选择、循环 三种语句

单行if

if(条件){若条件满足执行的代码}

if后面没有 ‘;’,如果加了,则{}内的代码总会执行

if (score >= 600) 
{ 
	cout << "分数大于等于600" << endl; 
}
if-else

if(){}else{}

多条件if

if(){}

else if (){}

else if(){}

三目运算符

c=(a<b?a:b)

返回的是一个变量,就是a,b之一

switch语句

switch( 表达式)

{

case 结果1: 执行语句 ;break;

case 结果2: 执行语句 ;break;

default: 执行语句 ; break;

}

while循环
while(循环条件)
{
    循环语句;
}
do-while循环

不管循环条件,上来先循环一遍

do
{
    循环语句
}  
while
{
    循环条件
}
for循环
for(起始表达式;条件表达式;末尾循环体)
{
    循环语句;
}

跳转语句

break语句

直接跳出当前循环体

数组

一维数组
//定义数组
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
一维数组名的意义

统计数组在内存中占的空间:

sizeof(数组名)

由于数组中元素都是一个类型的,所以每个元素都一样大!

统计数组中某一个元素的大小:

sizeof(数组名[位置])

查看数组的首地址:

//直接写数组的名字就是数字首地址
cout<<"数组首地址是: "<<arry<<endl;
//返回的是一个十六进制的数,可以通过(int)强行装换为十进制的数
cout<<"数组的首地址用十进制表示为:"<<(int)arry<<endl;

查看数组中某个元素的地址

$arry[0];

数组名是常量,不可修改

函数

指针

指针就是地址,就是一串存储内存地址的数字

定义指针
int *p;
p = &a
cout << p << endl;  //结果是这个内存的编号:0000009619B8F7C4  
cout <<*p << endl;  //结果就是p这个地址对应的值

&是取地址的符号,取到变量的地址

指针的类型取决于它这个地址里面的值对应的类型

查看指针所占内存空间

由于指针实际上是一个数字,所以占用的空间是固定的(仅取决于操作系统)

空指针

指向内存编号为0的指针,可以用来初始化指针,但是空指针指向的内存是不能访问的!

0~255的内存编号是系统的,不可以访问!下面的程序运行就会报错:

	int *p = NULL;
	*p = 100;
野指针

指向非法内存空间的指针,主动的指向一个内存地址就是非法的,只能请求!所以要避免出现野指针

int *p = (int *)0x110
//(int *) 可以将一个十六进制的数强行转换成指针类
const修饰指针

const 修饰的值不可以改

const int *p=&a; //*p对应的值不可以直接改,*p对应的是a对应的值,不是地址
int * const p =&a; //p的值不可以改,p是指地址

指针和数组

利用指针访问数组中的元素

指针是整形的,所以p++直接是p移动四个字节

指针和函数

值传递和地址传递

新函数的传入参数是地址,将这个地址中对应的值改变,则主函数中对应的值就会被改变

指针、数组和函数

结构体

结构体的定义和使用

结构体和其他量一样,既可以定义在主函数里面,也可以定义在主函数外面。仅仅是全局变量和局部变量的区别。

使用就是 结构体名 具体的结构体

通过面向对象符号 . 来访问

struct 结构体名 {结构体成员}

int main()
{
	struct Student
	{
		string name;
		int age;
		int score;
	};
	
	Student s1 ={"lanpangzi", 12, 99}; #创建一个具体的结构体
	cout << "name = " << s1.name << endl;
	cout << "everything all done" << endl;
	system("pause");
	return 0;
}

结构体数组
struct 结构类型名 数组名[元素个数] = { {}, {}, {} }

就跟普通数组的定义一样,只不过数组里面的元素类型是该结构体类型

int main()
{
	struct Student
	{
		string name;
		int age;
		int score;
	};
	
	struct Student stuarray[3]=
	{
		{"张三", 18, 90},
	    {"李四",17,90},
		{"王五",16,99}

	};
	stuarray[2].name = "lanpangzi";
	cout << "stuarray[2].name = " << stuarray[2].name <<endl;
	cout << "everything all done" << endl;
	system("pause");
	return 0;
}

结构体指针

创建一个结构体类型后和int那些数据类型一样,可以通过结构体类型名来定义同类型的指针

	struct Student
	{
		string name;
		int age;
		int score;
	};
		
	Student s = { "lanpanzgi",17, 98 };
	struct Student * p = &s; 

利用操作符 -> 可以通过结构体指针访问结构体属性

	cout << "p->name = " << p->name << endl;
结构体嵌套循环体

结构体中的元素是另一个结构体

struct student
	{
		string name;
		int age;
		int score;
	};
struct teacher 
{
	int id;
	string name;
	int age;
    student Bob;
};
#创建一个teacher对象并赋值
teacher t;
t.name = "Tony";
t.id = 123;
t.age = 32;
t.Bob.age =12;
结构体做函数参数

将结构体作为参数向函数中传递,传递方式分为值传递和地址传递两种

地址传递因为只是创建了一个新的指针,并没将传进来的结构体进行复制,会省略很多内存空间

传递的结构体一定要在主函数外面定义,否则子函数将无法识别此类型!

#值传递法,无法更改原结构体对象中的值
struct student  #一定要在外面定义
{
	string name;
	int age;
	int score;
};

void printstudent(student stu)
{
	cout << "子函数输出:stu.name: " << stu.name << endl;
}

int main()
{
    student stu;
	stu.age = 12;
	stu.name = "sat";
	stu.score = 90;
    printstudent(stu);
	cout << "everything all done" << endl;
	system("pause");
	return 0;
}

#地址传递
struct student  #一定要在外面定义
{
	string name;
	int age;
	int score;
};
void printstudent2(student *p)
{
	p->name = "lanpangzi";
}
int main()
{
	student stu;
	stu.age = 12;
	stu.name = "sat";
	stu.score = 90;
    cout << "stu更改前stu.name=" << stu.name << endl;
	printstudent2(&stu);
	cout << "stu更改后stu.name=" << stu.name << endl;
    cout << "everything all done" << endl;
	system("pause");
	return 0;
}
结构体中const的使用

可以通过const来防止误操作,通过const定义的变量,值不可改变

const更像是一个约束,可以对一个已经定义好的变量进行约束,这种用法尤其对子函数参数的定义使用,防止用户在子函数中改变值

void printstudent1(const student stu)
{
	stu.name = "sally";
	cout << "子函数输出:stu.name: " << stu.name;
}

C++核心编程

对c++面向对象编程技术进行介绍

内训分区模型

c++在程序执行时将内存分为4个区域:

代码区、全局区、栈区、堆区

程序运行前:

代码区:存放cpu执行的机器指令,代码区是共享的但只读的

全局区:存放全局变量和静态变量,还包含常量区(字符串常量和其他常量)

程序运行后:

栈区:函数的参数值,局部变量等(不要返回局部变量的地址,因为栈区的数据在函数执行完后会自动释放,返回来也没用)

堆区:在c++中使用new在堆区开辟一块内存,在子函数中使用new可以将本处于栈区的东西放在堆区

new操作符

堆区开辟数据,由程序员手动开辟,手动释放,释放关键字delete

new返回的值是该数据的指针

语法:new 数据类型

#include<iostream>
#include<string>
using namespace std;
int *fun()
{
    //在堆栈区创建单个变量
	int *p = new int(10);
	return p;
}

int main()
{

	int *p = fun();
	cout << "*p=" << *p << endl;
	//释放
	delete p;
	system("pause");
	return 0;
}
int * arr()
{
	//在堆区创建数组
	int *a = new int[10];
	for (int i = 0; i < 10; i++)
	{
		a[i] = i+100;
	}
	return a;
}
int main()
{	
    int *a =arr();
    for (int i = 0; i < 10; i++)
	{
	cout << a[i] << endl;;
	}

	//释放堆区数组
   //释放堆区数组要加 []
	delete[] a;
	system("pause");
	return 0;
}

引用

作用:给变量起别名

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

也就是两个名字对应同一块内存,跟指针差不多

引用必须初始化,且一旦初始化后就不可以更改了!

引用本质上是一个指针常量,归根到底是一个指针,但是这个指针指向的地址是不可更改的,地址对应的值倒是可以更改:

int& ref = a 相当于 int* const ref = &a

引用做函数参数

子函数的形参就是主函数参数的别名,只是刚好重名方便使用

相当于:

myswap(&a=a,&b=b)

void myswap(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10;
	int b = 6;
	myswap(a, b);
	cout << "a=" << a<<endl;
	system("pause");
	return 0;
}
引用做函数的返回值

不要返回局部变量的引用:

//int& 类型返回的是变量a ,而不是a对应的值!!
int& test01()
{
	int a = 0;
	return a;
}
int main()
{
	int& ref = test01();
	cout << "ref="<<ref<<endl;
    //第一次输出编译器保存了a,之后a会被销毁,所以第二次输出的就是错误的值了
	cout << "ref="<<ref<< endl;
	system("pause");
	return 0;
}

这种函数可以作为左值:

int& test01()
{
    //添加static可以将其设置为静态变量并放在全局区(并不是全局变量!),只在第一次定义的时候初始化,后面再运行时不会重新初始化!
	static int a = 0;
	return a;
}
int main()
{
	int& ref = test01();
	cout << "ref="<<ref<<endl;
	test01() = 1000;
	cout << "test01=" << test01() << endl;
	cout << "ref=" << ref << endl;

	system("pause");
	return 0;
}
常量引用

作用:防止形参修饰实参

void showvalue(const int& a)

{
	//这里面将无法对传进来的参数进行修改
}

引用的右值必须是变量,不能是数字:

int& ref=10; 将报错,但是 const int& ref = 10;就可以,因为这一步操作编译器自动进行了一步类似于 int temp = 10; const int& ref=temp;的操作,这样定义的引用没有原名

函数提高

函数的默认参数

语法:int func(int a=10, int b =2,int c = 3) { }

注意,如果某个位置已经有了默认值,则从这个位置往后都必须由默认值。且声明和实现只能有一个有默认参数

函数的占位参数

语法:void func(int) {} 形参只写类型不写变量名

占位的数在子函数中暂时是无法使用的

函数重载

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

前提:函数的名字相同,但是形参不完全相同。另外有默认值的也不行:

void func(int a)void func(int a,int b=10) 放一起,func(10)这样调用会出问题

类和对象

c++认为万物皆对象,封装、继承、多态

具有相同性质的对象称为类

使用案列:

#include<iostream>

using namespace std;


const double PI = 3.1415926;
//设计一个类,这个类的名字叫“Circle“
//创建类的关键字是class,后面紧接着是这个类的名字
class Circle  //没有括号也没有冒号
{
	//访问权限
public: //公共权限,注意是冒号! 也可以先使用变量后定义变量
	//属性
	int m_r;

	//行为
		//计算圆的周长
	double calculate()
	{
		return 2 * PI*m_r;
	}
};


int main()
{
	//通过上面定义的类来创建一个具体的求园的周长的对象(实例化)
	Circle cal_cir;
	//给创建的对象的属性进行赋值,
	cal_cir.m_r = 10;//用.进行访问
	cout << "圆的周长为:" << cal_cir.calculate() << endl;
	system("pause");
	return 0;
}
struct和class的区别

默认的权限不同,struct默认的权限是public,class默认的权限是private

成员属性设置为私有

将所有的成员都设置为私有的,然后通过定义共有函数来访问和修改这些私有成员,防止误操作!

类的封装

将类拆成头文件和源文件两大部分,对于大型项目尤其重要

1、首先创建空的头文件并命名(注意.h):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TIrm6ASg-1681634572042)(C:\Users\lanpangzi\AppData\Roaming\Typora\typora-user-images\image-20210619171529158.png)]

2、头文件中内容,注意头文件与源代码的不同,而且头文件中的函数只要声明不要操作,如下:

#pragma once //防止头文件重复包含
#include<iostream>
using namespace std;
//如果这里面使用了其他的类,需要包含那个类的头文件
class Point 
{
private:
	int m_x;
	int m_y;

public:
	void setX(int x);//没有具体的操作
	int getX();//没有具体的操作
	void setY(int y);//没有具体的操作
	int getY();//没有具体的操作
};

3、创建point类的空源文件并命名(注意后缀是.cpp)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCwC3ySu-1681634572043)(C:\Users\lanpangzi\AppData\Roaming\Typora\typora-user-images\image-20210619172053936.png)]

4、源文件中内容,注意开头包含上面创造的头文件,源文件中只有具体的操作没有声明,所以直接使用变量会报错,需要给定作用域(头文件中的)Point

#include "point.h"//已声明头文件,所以下面给定Point作用域才有效

//没有class等声明,只有具体的函数操作
void Point::setX(int x) //注意给定作用域
{
	m_x = x;
}
int Point::getX()
{
	return m_x;
}
void Point::setY(int y)
{
	m_y = y;
}
int Point::getY()
{
	return m_y;
}


5、要想使用某个类,只需在头文件中包含即可

#include<iostream>
#include<string>
//包含使用的类的头文件
#include "point.h"
#include "circle.h"
using namespace std;
各种默认的函数
对象的初始化和清理函数

构造函数用来初始化,析构函数是用来清理

构造函数语法:类名(){}

析构函数:~类名(){}

#include<iostream>
#include<string>


using namespace std;

//对象的初始化和清理

//构造函数 进行初始化
class Person
{
//任何操作都要要给作用域!
public:
	//构造函数函数名与类名相同,可以有参数,创建对象是该函数会被自动调用一次
	//如果用户不写,编译器会自动写一个Person(){},这个没有任何操作的函数进行初始化
	Person() 	//注意,虽然没有返回值,但是也不要写void
	{
		//每一次创建对象,Person()都会被调用一次
		cout << "构造函数被调用" << endl;
	}
	//析构函数 进行清理的操作
	//不能有参数,函数名与类名相同,在名称前加~,对象在销毁前会调用析构函数一次
	~Person()
	{
		cout << "析构函数被调用" << endl;
	}



};

void test01()
{
	Person p1;
}


int main()
{
	test01();//在函数中创建了对象,运行结束后对象就会被销毁,所以两个函数都会运行
	system("pause");
	return 0;
}
初始化列表

语法:Person(int a,int b,int c) : m_A{a}, m_B(b), m_C(c) {}

拷贝构造函数

作用:使用一个对象对另一个对象进行赋值时默认调用拷贝构造函数

#include<iostream>
#include<string>


using namespace std;

class Person
{
private:
	int p_age;
public:
	//无参构造函数
	Person()
	{
		cout << "无参构造函数已调用" << endl;
	}
	//有参构造函数
	Person(int age)
	{
		//调用构造函数
		cout << "调用构造函数age=" << age<<endl;
		p_age = age;
	}

	//拷贝构造函数, 当使用该类的对象给其他对象赋值时调用该函数
	Person(const Person &p) 	
	{
		p_age = p.p_age;		
		cout<< "拷贝构造函数被调用" << endl;
	}

	void print()
	{
		cout << "打印p_age = " <<p_age<< endl;
	}

};



int main()
{
	Person p3;//无参,调用的是无参构造函数
    //Person p();//这个不是无参,这是个函数声明!不会报错
	Person p1(12);//构造函数里面有参数,所以需要传参
	Person p2(p1);//等效于 p2=p1
        Person p4 = Person(12)//等于 Person p4(12);
        Person p5 = Person(p1)//等于 Person p5=p1;
	p2.print();//不可以将没有返回值的函数放到cout中
	system("pause");
	return 0;
}

Person(10);//匿名对象,执行完立即被销毁

深拷贝与浅拷贝

浅拷贝:简单的赋值操作 (编译器默认的是浅拷贝),容易造成堆区的内存重复释放

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


class Person
{
public:
	int p_age;
	int* h_height;

public:
	//有参构造函数
	Person(int age,int height)
	{
		//调用构造函数		
		p_age = age;   //此处p_age已经定义过了,不能重复定义了,否则会报错
       	       cout << "调用有参构造函数" <<endl;
		h_height = new int(height);
		cout << "构造函数内h_height=" << h_height << endl;
	}


	//拷贝构造函数
	Person(const Person &p) 	
	{	
		cout<< "拷贝构造函数被调用" << endl;
		p_age = p.p_age;
		// h_height=p.h_height; //这就是默认的浅拷贝代码
		// 深拷贝
		h_height = new int(*p.h_height);//新开辟一块内存
	}


	~Person()
	{
		//释放内存
		if (h_height != NULL)
		{
			delete h_height;
			h_height = NULL;  //标准删除方法
		}
		cout << "调用析构函数" << endl;
		system("pause");
	}

};

int main()
{
	Person p1(12, 78);
	Person p2(p1);
	cout << "p1.p_age=" << p1.p_age << "  *p1.h_height=" << *p1.h_height << endl;
	system("pause");
	return 0;
}
类对象作为类成员

一个类中的某个对象是另一个类 (即套娃)

class A {}
class B 
{
    A b;
}
静态成员函数与静态成员变量
  1. 静态成员函数不属于具体建立的对象,但是可以通过创建具体的对象来访问,也可以通过类名来直接访问。
class Person

{
    static  func();
}
//通过创建具体的对象来访问
Person p;
p.func();

//直接通过类名来访问
Person::func();

  1. 静态成员函数只能访问静态的成员变量,不能访问一般变量:
class Person
{
public:
    static int a;
    static func()
    {
        a=100;//一般函数这样写就会报错
    }
}
int Person::a=0;//静态成员函数要在外面申明
this指针

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

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

this指针的用途:

1)当形参和成员变量同名是,this指针可用来区分

2)当类中的函数要返回对象本身时,可使用return *this;

class Person
{
    public:
    Person(int age)
    {
        int age =age;//这样,默认,这三个age是一个东西,跟属性age没有任何关系
    }
    int age;       //这种写法是错误的,Person()构造函数里面的age和外面定义的age不是一个age,构造函数里面的定义对外面的age没有影响!
}
class Person
{
    public:
    Person(int age)
    {
        this->  age =age; //直接指向被调用的成员函数所属的对象,此处相当于p1->age=age,而p1->age就是p1内的属性age,就不会报错了
    }
    int age;      
}
int main()
{
    Person p1;
    system("pause");
    return 0;
}
#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	int age;
	Person(int age)
	{
		this->age = age;
	}
    //要返回函数的本体,则函数类型为Person&(用引用的方式进行返回)
    //Person&的方式返回的是此对象的引用,之后再更改更改的就是此对象的值,如果是Person的方式,返回的就是自动利用拷贝函数生成的新的Person对象,之后再操作原来对象中的age并不会被改变
	Person& PersonAddAge(Person p)
	{
		this->age += p.age;
		return *this;
    }
};

友元

在程序中,对于一些类里面的私有属性也想让类外的一些函数或类进行访问访问,就需要用到友元

友元,就是让一个函数或者类访问另一个类中的私有成员

友元的关键字:friend

友元的三种实现:

全局函数做友元、类做友元、成员函数做友元

1.全局函数做友元
#include<string.h>
#include<iostream>
using namespace std;

class Building
{
// 跟Build提前说声能访问的全局函数,
friend void goodGay();
public:
	Building()
	{
		m_BedRoom = "卧室";
		m_SittingRoom = "客厅";
	}
public:
	string m_SittingRoom;   //客厅,公有
private:
	string m_BedRoom;       //卧室。私有
};

void goodGay()
{
	Building building;
	cout << "好基友正在访问:" << building.m_SittingRoom << endl;
	cout << "好基友正在访问:" << building.m_BedRoom << endl;

}
int main()
{
	goodGay();
	cout << "Everything all done!" << endl;
	system("pause");
	return 0;
2.类做友元
include<string.h>
#include<iostream>
using namespace std;

class Building
{
	// 跟Build提前说声能访问的类
	friend class Goodgay;

public:
	Building()
	{ 
		m_BedRoom = "卧室";
		m_SittingRoom = "客厅";
	}
public:
	string m_SittingRoom;   //客厅,公有
private:
	string m_BedRoom;       //卧室。私有
};

class Goodgay
{
public:
	Building building; 
	void visit()
	{
		cout << "好基友类正在访问: " << building.m_SittingRoom << endl;
		cout << "好基友类正在访问: " << building.m_BedRoom << endl;
	}
};
int main()
{
	Goodgay gg;
	gg.visit();
	cout << "Everything all done!" << endl;
	system("pause");
	return 0;
}
3.其他类中的成员函数做友元
#include<string.h>
#include<iostream>
using namespace std;

class Building
{
	// 跟Build提前说声能访问的成员函数及其所在的类
	friend void Goodgay::visit();

public:
	Building()
	{ 
		m_BedRoom = "卧室";
		m_SittingRoom = "客厅";
	}
public:
	string m_SittingRoom;   //客厅,公有
private:
	string m_BedRoom;       //卧室。私有
};

class Goodgay
{
public:
	Building building; 
	void visit()//Goodgay中只有这个成员函数能访问Building中的s
	{
		cout << "好基友类正在访问: " << building.m_SittingRoom << endl;
		cout << "好基友类正在访问: " << building.m_BedRoom << endl;
	}
};
int main()
{
	Goodgay gg;
	gg.visit();
	cout << "Everything all done!" << endl;
	system("pause");
	return 0;
}
运算符重载

概念:对已有的运算符重新进行定义,以适应不同的数据类型之间的运算

1.加号运算符重载

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

方式:(1)成员函数重载,写在类的定义中

​ (2)全局函数重载,写在main外面

#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	//通过成员函数重载,函数名必须使用operator+
	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;
};

int main() 
{
	Person p1;
	p1.m_a = 10;
	p1.m_b = 5;
	Person p2;
	p2.m_a = 10;
	p2.m_b = 10;
	Person p3 = p1 + p2;   //本质上是调用函数operator+函数,但是可以简写成一个加号
	cout << "p3.m_a=" << p3.m_a << "p3.m_b=" << p3.m_b << endl;
	system("pause");
	return 0;
}


#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	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;
}

int main() 
{
	Person p1;
	p1.m_a = 10;
	p1.m_b = 5;
	Person p2;
	p2.m_a = 10;
	p2.m_b = 10;
	Person p3 = p1 + p2;
	cout << "p3.m_a=" << p3.m_a << "p3.m_b=" << p3.m_b << endl;
	system("pause");
	return 0;
}


2.左移运算符 ‘ << ’ 重载

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

方法:利用全局函数重载

#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	int m_a;
	int m_b;
};


//通过全局函数重载<<,cout对象只能有一个,所以要用引用定义
ostream& operator<<(ostream &cout, Person &p) //cout是ostream对象
{
	cout << "p.m_a=" << p.m_a << " p.m_b=" << p.m_b;
	return cout;
}

int main() 
{
	Person p1;
	p1.m_a = 10;
	p1.m_b = 5;

	cout << p1 << endl; //定义的那个函数必须要有返回值,否则endl不能加在后面
	system("pause");
	return 0;
}


但是这种方法当m_a和m_b是私有属性时将出错,所以使用友元技术来规避

#include<string.h>
#include<iostream>
using namespace std;

class Person
{
	friend ostream& operator<<(ostream& cout, Person& p);//y
public:
	Person(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
private:
	int m_a;
	int m_b;
};


//通过全局函数重载<<,cout对象只能有一个,所以要用引用定义
ostream& operator<<(ostream &cout, Person &p) //cout是ostream对象
{
	cout << "p.m_a=" << p.m_a << " p.m_b=" << p.m_b;
	return cout;
}

int main() 
{
	Person p1(10,10);
	cout << p1 << endl; //定义的那个函数必须要有返回值,否则endl不能加在后面
	system("pause");
	return 0;
}


3.递增运算符的重载

作用:对自定义的数据类型实现++a和a++的操作

方法:成员函数重载,写在类的定义中

#include<string.h>
#include<iostream>
using namespace std;

class Myinteger
{
	friend ostream& operator<<(ostream& cout, Myinteger p);
public:
	Myinteger()
	{
		m_num = 0;
		}
//前置++重加载
	//前置返回的是引用,后置返回的是本身
	Myinteger& operator++()
	{
		//先++再返回
		m_num++;
		return *this;
	}
//后置++重加载
	//int占位用来给编译器区分前置后置
	Myinteger operator++(int)
	{
		//记录下原来的值,++后返回原值
		Myinteger temp = *this;
		m_num++;
		return temp;
	}




private:
	int m_num;
};

//重加载<<以便于cout中输出
ostream& operator<<(ostream& cout, Myinteger p)
{
	cout << p.m_num;
	return cout;
}

int main()
{
	Myinteger myint;
	cout << ++(++myint) << endl;
	cout << myint << endl;
	system("pause");
	return 0;

}
4.赋值运算符重载

作用:防止对同一堆区内存重复释放

方法:写在类的定义中

#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		m_age = new int(age);
	}
	//析构函数,释放掉分配的堆区空间
	~Person()
	{
		if (m_age !=NULL)
		{
			delete m_age;
			m_age = NULL;
		}
	}

    //返回的是Person引用对象,这样这个东西指向的才是传进来的参数本身
	Person& operator=(Person &p)
	{
		//先判断是否有属性在堆区,有的话先释放
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		//深拷贝
		m_age = new int(*p.m_age);
		return *this;//返回对象地址的值就是返回对象本身,
	}
	int* m_age;
};

int main()
{
	Person p1(10);
	Person p2(5);
	Person p3(2);
	p2 = p1;
	cout << "*p1.m_age=" << *p1.m_age << endl;
	cout << "*p2.m_age=" << *p2.m_age << endl;
	cout << "*p3.m_age=" << *p3.m_age << endl;

	//没有重加载那个返回值的话,p2就不是个Person对象了了
	p3 = p2 = p1;
	cout << "*p1.m_age=" << *p1.m_age << endl;
	cout << "*p2.m_age=" << *p2.m_age << endl;
	cout << "*p3.m_age=" << *p3.m_age << endl;
	system("pause");
	return 0;
}
5.比较运算符重载

注意:比较运算符返回的都是布尔值,定义时使用bool 命名

#include<string.h>
#include<iostream>
using namespace std;

class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}

	//比较运算符返回的都是布尔值
	bool operator==(Person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return true;
		}
		else
		{
			return false;
		}
		
	}

	string m_name;
	int m_age;
};

int main()
{
	Person p1("Tom", 18);
	Person p2("Jerry", 18);
	if (p1 == p2)
	{
		cout << "p1与p2相等" << endl;
	}
	else
	{
		cout << "p1与p2不相等" << endl;
	}
	system("pause");
	return 0;
}
6.函数调用运算符“()”重载

重载后的()使用与函数的调用非常相似,因此也称之为仿函数,仿函数没有固定的写法,非常灵活

#include<string.h>
#include<iostream>
using namespace std;


class Myprint
{
public:
	//好像()重载后就不能定义构造函数了
	//Myprint myprint(string text)
	//{
	//	m_text = text;
	//}

	//这个()的作用就是直接打印传进来的string
	void operator()(string text)
	{
		cout << text << endl;
	}
	string m_text;
};

int main()
{
	Myprint p1;
	p1("Jerry");
	system("pause");
	return 0;

}
类的继承

继承是面向对象的三大特征之一,利用继承在定义下级对像时可以减少工作量。

无论是哪种继承方式,子类都可以继承父类所有的属性,但是不一定能访问到

使用方式: class 子类 : 继承方式 父类{}

查看一个类的结构: 在开发人员命令提示工具(Developer Command Prompt for VS 2019)中 cd进要查看的类的文件所在的文件夹—>输入 cl /d1 reportSingleClassLayout类名 类所在的文件名 —>>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nanvhoKK-1681634572043)(E:\博客文件\c++学习笔记.assets\kG6VphagoOyADt1.png)]

#include<string.h>
#include<iostream>
using namespace std;

//类的继承

class Basecode
{
public:
	void my_basecode()
	{
		cout << "所有的其他类都要调用我!" << endl;
	}
};

//加上 : public 主类名就可以使次类继承主类的性质
class Subclass1 : public Basecode
{
public:
	void my_subclass1fun()
	{
		cout << "1号次类的函数" << endl;
	}
};


class Subclass2 : public Basecode
{
public:
	void my_subclass2fun()
	{
		cout << "2号次类的函数" << endl;
	}
};

int main()
{
	Subclass1 sub1;
	Subclass2 sub2;
	sub1.my_basecode();
	sub1.my_subclass1fun();
	sub2.my_subclass2fun();

	system("pause");
	return 0;
}
1.继承方式

类的继承方式一共有3种:

公共继承、保护继承、私有继承,区别如下:

2.继承中的构造和析构顺序

父类构造函数先有,子类构造函数后有,子类析构函数先有,父类析构函数后有

及:

父类先生成后销毁,子类后生成先销毁

3.父类和子类中的同名成员

子类中的直接访问,父类中的要加作用域

//父类:
class Base
{
    public:
    int m_a=100;
}

//子类:
class Son : public Base
{
    public:
    int m_a=200;
}

int main()
{
    Son s1;
    //访问子类中的m_a
    s1.m_a;
    //访问父类中的m_a
    s1.Base::m_a;
}

成员函数操作一样

4.多继承语法

含义:允许一个子类继承多个父类

class Son : public Father1, protect Farther2..........{}

多继承非常容易发生父类中类重名的情况,要注意使用作用域。c++不建议使用多继承!

5.菱形继承与虚继承

两个子类继承同一个父类,又一个孙子类同时继承这两个子类

由此可知,菱形继承中的孙子类中的没个成员都有两份且每次使用时都要给定作用域,这很麻烦,使用虚继承可以解决这个问题

#include<string.h>
#include<iostream>
using namespace std;

//菱形继承与虚继承

class Animal
{
public:
	int m_age;
};

//使用 virtual关键字是按虚继承
class Sheep : virtual public Animal {};

class Tuo : virtual public Animal {};

class Sheeptuo : public Sheep, public Tuo {};
int main()
{
	Sheeptuo st1;
	//这样 Sheeptuo中只有一个m_age成员,使用作用域后得到的也都只是指向这个m_aged
	st1.m_age = 10;
	cout << "st1.m_age=" << st1.m_age << endl;
	system("pause");
	return 0;
}

类的多态

多态是c++面向对象的三大特性之一

1.多态的基本概念

多态的使用原因:定义虚函数是为了允许用基类的指针来调用子类的这个函数。

多态的分类:静态多态、动态多态

两种多态的区别:静态的在编译阶段就已经确定函数的地址等,动态的在运行阶段才确定

关键:父类指针指向子类对象,这个指针实际上还是子类的东西

Base *s1 = new Son;

案列:

#include<string.h>
#include<iostream>
using namespace std;

//动态多态

class Animal
{
public:
	//加virtua 实现动态多态
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

class Cat : public Animal
{
public:
	void speak()
	{
		cout << "猫在说话" << endl;
	}
};

//当用父类的指针指向子类的对象的时候,调用同名函数时若无上面的
//virtual 则默认调用父类中的函数
void doSpeak(Animal& animal)
{
	animal.speak();
}


int main()
{
	Cat cat;
	doSpeak(cat);
	system("pause");
	return 0;
}

利用多态实现计算器:

#include<string.h>
#include<iostream>
using namespace std;

//利用多态实现加减运算
// 利用多态的优点:
//1、更方便后期的扩展
//2、可读性强,每一个功能都分开了

//定义抽象的计算机类,它实现任何功能,
// 只是定义最终要结果的虚函数并接受两个数
class Abstractcalculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int m_a;
	int m_b;
};

//定义实现+的类
class Addcalculator : public Abstractcalculator
{
       public:
	virtual int getResult()
	{
		return m_a + m_b;
	}
};

//定义实现-的类
class Subcalculator : public Abstractcalculator
{
    public:
	virtual int getResult()
	{
		return m_a - m_b;
	}
};


int main()
{
	//+
	//多态实现的关键一步,父类指针指向子类对象
	Abstractcalculator* add = new Addcalculator;
	add->m_a = 10;
	add->m_b = 12;
	cout << add->m_a << "+" << add->m_b << "=" << add->getResult() << endl;
        delete add
	
	//-
	//多态实现的关键一步,父类指针指向子类对象
	Abstractcalculator* sub = new Subcalculator;
	sub->m_a = 10;
	sub->m_b = 12;
	cout << sub->m_a << "+" << sub->m_b << "=" << sub->getResult() << endl;
        delete sub

	system("pause");
	return 0;
}

2.纯虚函数和抽象类

多态中,通常父类中的虚函数是没用的,因此可以用纯虚函数代替,反正用的也是子类中的函数。

当一个类中出现了纯虚函数,则这个类就成为了抽象类,它不可以实列化对象,在子类中必须重写这个函数

实现:virtual int fun()=0;

#include<string.h>
#include<iostream>
using namespace std;

//纯虚函数与抽象类
class Base
{
public:
	virtual void fun() = 0;
};

class Son : public Base
{
	virtual void fun()
	{
		cout << "纯虚函数在子类中的的调用" << endl;
	}
};
int main()
{
	//注意,多态需要父类指针指向子类对象
	Base * s1=new Son;
	s1->fun();
	//new出来的对象要及时删除!
	delete s1;
	system("pause");
	return 0;
}

3.多态的应用案例

煮饮料,对于煮咖啡和煮茶都具有相同的步骤,所以定义一个父类

#include<iostream>
#include<string>
using namespace std;

//利用多态实现饮料的冲泡
class Abstractdring
{
public:
	virtual void boil() = 0;
	virtual void brew() = 0;
	virtual void pourInCup() = 0;
	virtual void putSomething() = 0;

	virtual void makeDring()
	{
		boil();
		brew();
		pourInCup();
		putSomething();
	}
};

class Coffee : public Abstractdring
{
public:
	virtual void boil()
	{
		cout << "咖啡开始煮水" << endl;
	}
	virtual void brew()
	{
		cout << "咖啡开始冲泡" << endl;
	}
	virtual void pourInCup()
	{
		cout << "咖啡开始倒入杯中" << endl;
	}
	virtual void putSomething()
	{
		cout << "咖啡加入一些调味品" << endl;
	}

	virtual void makeDring()
	{
		boil();
		brew();
		pourInCup();
		putSomething();
	}
};


class Tea : public Abstractdring
{
public:
	virtual void boil()
	{
		cout << "茶开始煮水" << endl;
	}
	virtual void brew()
	{
		cout << "茶开始冲泡" << endl;
	}
	virtual void pourInCup()
	{
		cout << "茶开始倒入杯中" << endl;
	}
	virtual void putSomething()
	{
		cout << "茶加入一些枸杞" << endl;
	}

	virtual void makeDring()
	{
		boil();
		brew();
		pourInCup();
		putSomething();
	}
};

void doWork(Abstractdring* abs)
{
	abs->makeDring();
	delete abs;
}

int main()
{
	//直接new了一个Coffee对象,这个对象没有名字,但是使用后还是要释放
	doWork(new Coffee);
	cout << "---------------------" << endl;
	doWork(new Tea);
	system("pause");
	return 0;
}
4. 虚析构和纯虚析构

作用:使用多态时,若子类有数据在堆区(new出来的)则父类在释放时释放不了子类在堆区的数据,造成内存浪费

实现:

虚析构

在父类的析构函数前加 virtual

virtual ~Aniaml()

{

函数实现

}

纯虚析构:与纯虚函数一样,但是需要在类外加实现申明:

class Animal

{...

...

virtual ~Animal()=0;

};

Animal::~Animal()

{

函数实现

}

虚析构例子:

#include<iostream>
#include<string>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "父类构造函数" << endl;
	}

	void virtual speak() = 0;
    
	//当父类析构是虚析构时,delete 父类
	//时才会涉及到子类的析构函数
	virtual ~Base()
	{
		cout << "父类析构函数" << endl;
	}
};

class Son:public Base
{
public:
	Son(string name)
	{
		n_name = new string(name);
		cout << "子类构造函数" << endl;
	}

	void virtual speak() 
	{
		cout << *n_name << "小猫在说话" << endl;
	}
	~Son()
	{
		if (n_name != NULL)
		{
			//由于使用了new,要及时delete
			delete n_name;
			cout << "子类析构函数" << endl;
		}
	}

	string* n_name;

};

int main()
{
	//多态的实现,父类指针指向子类对象
	Base* b1 = new Son("TOM");
	b1->speak();
	//父类指针在删除时不会涉及到子类的析构代码,
	//所以new在Son中的数据不会被删除从而浪费内存
	delete b1;


	system("pause");
	return 0;
}

5. 案列-电脑组装
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

//创建CPU、显卡、内存三个抽象类
class Cpu
{
public:
	virtual void calculate() = 0;
};
class Graphiccard
{
public:
	virtual void display() = 0;
};
class Memory
{
public:
	virtual void storage() = 0;
};

//创建电脑类
class Computer
{
public:
	Computer(Cpu* cpu,Graphiccard* graphiccard,Memory* memory)
	{
		m_cpu=cpu;
		m_graphiccard=graphiccard;
		m_memory= memory;
	}
	void work()
	{
		m_cpu->calculate();
		m_graphiccard->display();
		m_memory->storage();
	}
	//由于传进来了堆区的文件,一定要记得在析构函数中释放
	~Computer()
	{
		if (m_cpu!= NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_graphiccard != NULL)
		{
			delete m_graphiccard;
			m_graphiccard = NULL;
		}
		if (m_memory != NULL)
		{
			delete m_memory;
			m_memory = NULL;
		}
	}
private:
	Cpu* m_cpu;
	Graphiccard* m_graphiccard;
	Memory* m_memory;
};

//创建两家厂商,每家厂商都可以单独制造这三种零件
class Lenovocpu:public Cpu
{
public:
	virtual void calculate()
	{
		cout<< "联想的CPU正在工作" << endl;
	}
};
class Lenovogpu :public Graphiccard
{
public:
	virtual void display()
	{
		cout << "联想的显卡正在工作" << endl;
	}
};

class Lenovomemory :public Memory
{
public:
	virtual void storage()
	{
		cout << "联想的内存条正在工作" << endl;
	}
};

class Intelcpu : public Cpu
{
public:
	virtual void calculate()
	{
		cout << "Intel的CPU正在工作" << endl;
	}
};
class Intelgpu : public Graphiccard
{
public:
	virtual void display()
	{
		cout<< "Intel的显卡正在工作" << endl;
	}
};

class Intelmemory :public Memory
{
public:
	virtual void storage()
	{
		cout << "Intel的内存条正在工作" << endl;
	}
};


void test01()
{
	cout << "创建第一台电脑" << endl;

	//第一台电脑
	Computer* computer_01= new Computer(new Intelcpu, new Intelgpu, new Intelmemory);
	computer_01->work();
	//开辟到堆区的文件一定记得释放
	delete computer_01;
	computer_01 = NULL;
	cout << "============================" << endl;
	cout << "创建第二台电脑" << endl;

	//第二台电脑
	Computer* computer_02 = new Computer(new Lenovocpu, new Lenovogpu, new Intelmemory);
	computer_02->work();
	//开辟到堆区的文件一定记得释放
	delete computer_02;
	computer_02 = NULL;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

文件操作

​ 程序在运行过程中产生的数据都是临时的,程序已结束就会消失,所以需要主动保存文件,c++中将文件分为两类,文本文件和二进制文件(也就是我们常用的.mp4,.mp3等直接读不懂的文件),对文件的操作需要用到头文件<fstream>.

​ 对文件的操作分为三类:读ofstream、写ifstream、读写fstream

1. 文本文件
1. 写文本文件

写文件分为4步:创建流对象ofstream ofs->打开文件ofs.open("文件路径",打开方式)->写入文件ofs<<"文件内容"->关闭文件ofs.close()

打开文件的方式:

打开方式作用
ios::in读打开
ios::out写打开
ios::ate在文件末尾开始
ios::app以追加的方式开始
ios::trunc如果已存在文件直接删除
ios::binary二进制方式

注意:如果要多重方式打开文件,用 | 操作符

用二进制方式写文件:ofs.open("E:\电影\风骚律师",ios:binary|ios::out);

#include<iostream>
#include<fstream>
using namespace std;

void test01()
{
	ofstream ofs;//这个对象是从内存写到磁盘,所以是ofstream
	ofs.open("test01.txt", ios::out);
	ofs << "这里是test01!";
}

int main()
{
	test01();
	system("pause");
	return 0;
}
2.读文件

读文件分为5步:创建流对象ifstream ifs->打开文件ifs.open("文件路径",打开方式)->ifs.is_open()判断是否打开成功->4种方式读取数据->关闭文件ifs.close()

读取文件的方式有4种,分别为:

  1. 用>>运算符 :

    	//创建一个数组用来存放读取的文件
    	char buf[1024] = { 0 };
    	while (ifs >> buf)
    	{
    		cout << buf << endl;
    	}
    
  2. 用getline()

  	char buf[1024] = { 0 };
  	while (ifs.getline(buf,sizeof(buf)))
  	{
  		cout << buf << endl;
  	}
  1. getline+string

    	//创建一个string用来存放读取的文件
    	string buf;
    	while (getline(ifs,buf))
    	{
    		cout << buf << endl;
    	}
    
  2. EOF+get (不推荐使用这种方法)

    	//创建一个char用来一个一个的存放字符
    	char c;
    	while ((c=ifs.get())!=EOF)
    	{
    		cout << c;
    	}
    
    #include<iostream>
    #include<fstream>
    #include<string>
    using namespace std;
    
    void test01()
    {
    	ifstream ifs;
    	ifs.open("test01.txt", ios::in);
    	//判断是否打开
    	if (!ifs.is_open())
    	{
    		cout << "文件打开失败" << endl;
    		return;
    	}
    
    	//创建一个char用来一个一个的存放字符
    	char c;
    	while ((c=ifs.get())!=EOF)
    	{
    		cout << c;
    	}
    
    	ifs.close();
    
    }
    
    int main()
    {
    	test01();
    	system("pause");
    	return 0;
    }
    
2. 二进制文件
1. 写文件

可以将任意形式的文件写进磁盘,此处将一个Person类写进TXT文档

#include<iostream>
#include<fstream>
#include<string>
using namespace std;

class Person
{
public:
	char m_name[64] = { 0 };
	int m_age = 0;
};
void test01()
{
       //此处类的赋值方式与struct相同
	Person p1 = { "Bob",18 };
	ofstream ofs( "Person_p1.txt",ios::out | ios::binary );
	//ofs.write()的第一个参数必须是const char*类型,所以要强制转换
	ofs.write((const char*)&p1, sizeof(Person));
	ofs.close();

}

int main()
{
	test01();
	system("pause");
	return 0;
}
2.读文件

除了要用read(),其他与文本文件操作相同

#include<iostream>
#include<fstream>
#include<string>
using namespace std;

class Person
{
public:
	char m_name[64] = { 0 };
	int m_age = 0;
};
void test01()
{
	Person p1;
	ifstream ifs( "Person_p1.txt",ios::in | ios::binary );
	//ifs.read()的第一个参数必须是char*类型,所以要强制转换
	ifs.read((char*)&p1, sizeof(Person));
	ifs.close();
	cout << p1.m_age << endl;

}

int main()
{
	test01();
	system("pause");
	return 0;
}

案列

1.职工管理系统

要实现的功能:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ni5ZopU-1681634572045)(E:\博客文件\c++学习笔记.assets\41uOhD.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9qqC8hu-1681634572045)(E:\博客文件\c++学习笔记.assets\41Kejs.png)]

效果图:

41KwE6.png

1) 创建职工管理类

这是一个大类,里面包含了所有的操作,但是它不具体实现

新建一个头文件和一个cpp文件,并声明好:

//文件名:workerManager.h
#pragma once //防止一个头文件被重复包含
#include<iostream>
using namespace std;

class workerManager
{
public:
	workerManager();//构造函数
	~workerManager();//析构函数
};

//文件名:workerManager.cpp
#include"workerManager.h"

workerManager::workerManager()
{
}
workerManager::~workerManager()
{

}
2) 菜单功能

显示菜单,与用户进行沟通

  1. 方法

在workManager.h中创建showMenu()函数,但是不具体实现,在workManager.cpp中具体实现

//文件名:workerManager.h
#pragma once //防止一个头文件被重复包含
#include<iostream>
using namespace std;

class workerManager
{
public:
	workerManager();//构造函数
	void showMenu();//显示菜单
	~workerManager();//析构函数
};

//文件名:workerManager.cpp
#include"workerManager.h"

workerManager::workerManager()
{
}

void workerManager::showMenu()//给showMenu具体的实现
{
	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;
}
workerManager::~workerManager()
{

}
  1. 测试结果

    在主函数职工管理系统.cpp中新建main()进行测试:

    //文件名:职工管理系统.cpp
    #include<iostream>
    #include"workerManager.h"
    using namespace std;
    
    int main()
    {
    	//创建一个管理对象
    	workerManager worker_manager0;
    	worker_manager0.showMenu();
    	system("pause");
    	return 0;
    }
    
3) 退出功能

将各个0-7的功能放在一个while循环中,通过switch语句判断用户操作。另外,在workerManager.h中添加退出函数的声明,在workerManager.cpp中添加退出函数的实现

//文件名:workerManager.cpp,加一个函数
void workerManager::exitSystem()
{
	cout << "欢迎下次使用!" << endl;
	system("pause");
	exit(0);
}
//文件名:职工管理系统.cpp
#include<iostream>
#include"workerManager.h"
using namespace std;

int main()
{
	//创建一个管理对象
	workerManager worker_manager0;
	int choice = 0;
	while (true)
	{
		worker_manager0.showMenu();
		cout << "请输入你的选择:" << endl;
		cin >> choice;
		switch (choice)
		{
		case 0: //退出管理程序
			worker_manager0.exitSystem();
		case 1://增加职工信息
			break;
		case 2://显示职工信息 
			break;
		case 3://删除离职职工
			break;
		case 4://修改离职职工
			break;
		case 5://查找离职职工
			break;
		case 6://按照编号排序
			break;
		case 7://清空所有文档
			break;
		default:
			system("cls");
			break;
		}
	}

	system("pause");
	return 0;
}
4) 创建职工类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQyqlEzh-1681634572046)(E:\博客文件\c++学习笔记.assets\y1kIrwPoWOLQJVx.png)]

新建worker.h头文件,将员工相关操作和信息放在这里,但是不做任何实现,具体的实现放在比worker类低一级的employee、manager、boss类中,这三种类对应三种职工种类,且都是头文件声明,源文件实现!跟上面的workManager一样。

5) 添加员工类

直接在workerManager.h中添加相关成员属性(变量)和函数:

//文件名:workerManager.h
#pragma once //防止一个头文件被重复包含
#include<iostream>
#include"worker.h"
#include<string>
using namespace std;

class workerManager
{
public:
	workerManager();//构造函数
	void showMenu();//显示菜单
	void exitSystem();//0退出程序
	void add_emp();//添加员工

	int m_emp_num;//文件中的人数
	Worker** m_emp_arry;//员工数组指针
	~workerManager();//析构函数
};

接下来在workerManager.cpp中初始化变量和定义函数:

//文件名:workerManager.cpp
#include"workerManager.h"
#include"employee.h"
#include"manager.h"
#include"boss.h"

//主程序的构造函数,用来初始化成员属性
workerManager::workerManager()
{
	//初始化成员属性
	this->m_emp_num = 0;
	this->m_emp_arry = NULL;
}
//显示主菜单
void workerManager::showMenu()//给showMenu具体的实现
{
	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;
}

//0,退出
void workerManager::exitSystem()
{
	cout << "欢迎下次使用!" << endl;
	system("pause");
	exit(0);
}

//1,添加员工
void workerManager::add_emp()
{
	cout << "请输入要添加的员工数量:" << endl;
	int add_num = 0;
	cin >> add_num;
	if (add_num > 0)
	{
		int new_size = this->m_emp_num + add_num;
		//创建更长的数组来暂时存放全部数据
		//最后还是要指向原来的地址
		Worker** new_space = new Worker* [new_size];
		if (this->m_emp_arry != NULL)
		{
			for (int i = 0; i < this->m_emp_num; i++)
			{
				//将东西复制进新的数组
				new_space[i] = this->m_emp_arry[i];
			}
		}

		//输入新数据
		for (int i = 0; i < add_num; i++)
		{
			int id;
			string name;
			int depid;
			cout << "请输入第" << i + 1 << "位员工的信息!" << endl;
			cout << "请输入第" << i + 1 << "位员工的编号: " << endl;
			cin >> id;
			cout << "请输入第" << i + 1 << "位员工的姓名: " << endl;
			cin >> name;
			cout << "请选择第" << i + 1 << "位员工的部门岗位: " << endl;
			cout << "1.普通员工" << endl;
			cout << "2.经理" << endl;
			cout << "3.老板" << endl;
			cin >> depid;

			Worker* worker = NULL;
			switch (depid)
			{
			case 1:
				worker = new Employee(id,name,depid);
				break;
			case 2:
				worker = new Manager(id, name, depid);
				break;
			case 3:
				worker = new Boss(id, name, depid);
				break;
			default:
				break;
			}
			new_space[this->m_emp_num + i] = worker;
	    }
		//释放原空间
		delete[] this->m_emp_arry;
		//重新定向到新生成的存放全部数据的数组
		this->m_emp_arry = new_space;
		//更新个数
		this->m_emp_num = new_size;
		cout << "已成功添加" << add_num << "名新员工!" << endl;
	}
	else
	{
		cout << "输入有误" << endl;
	}
	//添加完成后要单击继续然后清屏
	//之后才进入下一次大循环
	system("pause");
	system("cls");
}
  workerManager::~workerManager()
{
	//析构函数释放掉创建在堆区的内容
	if (this->m_emp_arry != NULL)
	{
		delete[] this->m_emp_arry;
		this->m_emp_arry = NULL;
	}

}


6) 截止目前所有文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jaBL2v4o-1681634572047)(E:\博客文件\c++学习笔记.assets\FjMcqmklySDrRQU.png)]

workmanager的内容如上,其他的:

//worker.h
#pragma once
#include<string>
#include<iostream>
using namespace std;

class Worker
{
public:
	//纯虚函数,不具体实现
	virtual void  showInfo() = 0;
	virtual string getDeptName() = 0;

	//定义员工基本的属性
	int m_ID;//职工ID编号
	string m_name;//职工姓名
	int m_deptID;//职工所在部门ID

};
//manager.h
#pragma once
#include"worker.h"

class Manager :public Worker
{
public:
	//构造函数
	Manager(int id, string name, int depid);
	//虚函数,不具体实现
	virtual void  showInfo();
	virtual string getDeptName();
	//不用定义成员属性!

};
//manager.cpp
#include"manager.h"

Manager::Manager(int id, string name, int depid)
{
	this->m_ID = id;
	this->m_name = name;
	this->m_deptID = depid;
}

void Manager::showInfo()
{
	cout << "职工编号: " << this->m_ID
		<< "\t职工姓名: " << this->m_name
		<< "\t职工岗位: " << this->getDeptName()
		<< "\t岗位职责: 完成老板布置的任务。" << endl;
}

string Manager::getDeptName()
{
	return string("经理");
}
//employee.h
#pragma once
#include"worker.h"

class Employee:public Worker
{
public:
	//构造函数
	Employee(int id, string name, int deptid);
	//虚成员函数
	virtual void  showInfo();
	virtual string getDeptName();
};



//employee.cpp
#include"employee.h"
//对employee.h中的Employee类进行实现
Employee::Employee(int id,string name,int deptid)
{
	this->m_ID = id;
	this->m_name = name;
	this->m_deptID = deptid;
}

void Employee::showInfo()
{
	cout << "职工编号: " << this->m_ID
		<< "\t职工姓名: " << this->m_name
		<< "\t职工岗位: " << this->getDeptName()
		<< "\t岗位职责: 完成经理布置的任务。" << endl;
};

string Employee::getDeptName()
{
	return string("员工");
}
//boss.h
#pragma once
#include"worker.h"
class Boss :public Worker
{
public:
	Boss(int id,string name,int depid);
	virtual void  showInfo();
	virtual string getDeptName();
};
//boss.cpp
#include"boss.h"
Boss::Boss(int id, string name, int depid)
{
	this->m_ID = id;
	this->m_name = name;
	this->m_deptID = depid;
}

void Boss::showInfo()
{
	cout << "职工编号: " << this->m_ID
		<< "\t职工姓名: " << this->m_name
		<< "\t职工岗位: " << this->getDeptName()
		<< "\t岗位职责: 计划所有的任务。" << endl;
}

string Boss::getDeptName()
{
	return string("大老板");
}

主函数:

//职工管理系统.cpp
#include<iostream>
#include"workerManager.h"
#include"employee.h"
#include"manager.h"
#include"boss.h"
using namespace std;

int main()
{

	//创建一个管理对象
	workerManager worker_manager0;
	int choice = 0;
	while (true)
	{
		worker_manager0.showMenu();
		cout << "请输入你的选择:" << endl;
		cin >> choice;
		switch (choice)
		{
		case 0: //退出管理程序
			worker_manager0.exitSystem();
		case 1://增加职工信息
			worker_manager0.add_emp();
		case 2://显示职工信息 
			break;
		case 3://删除离职职工
			break;
		case 4://修改离职职工
			break;
		case 5://查找离职职工
			break;
		case 6://按照编号排序
			break;
		case 7://清空所有文档
			break;
		default:
			system("cls");
			break;
		}
	}

	system("pause");
	return 0;
}
7) 文件交互-保存与读取
  1. 写文件

首先在workermanager.h里面添加添加宏常量并包含文件操作的头文件,然后再workmanager.cpp中实现

workermanager.cpp中savaFile()函数实现如下:

void workerManager::saveFile()
{
	ofstream ofs;
	ofs.open(FILENAME, ios::out);
	for (int i = 0; i < this->m_emp_num; i++)
	{
		ofs << this->m_emp_arry[i]->m_ID << " "
			<< this->m_emp_arry[i]->m_name << " "
			<< this->m_emp_arry[i]->m_deptID << endl;
	}
	ofs.close();
}

注意在workermanager.cpp的addEmp()函数添加成功后的代码后加上this->saveFile();

  1. 读回来
    读的时候文件有三种情况(对于文件被删了部分的情况暂不考虑):
    1>程序被第一次调用,还没生成;
    2>已经生成了但是里面东西被删了
    3>已经生成了而且东西完好

读的操作在创建workermanager对象时就要进行,因此将其放在workermanager的构造函数中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值