C++面向对象速览

感谢黑马开源!

一,重载函数

(同一作用域,相同名称,不同参数的函数)

注意事项:

1,(引用/指针)也可以作为重载的条件

传递方式是否可以区分 const原因
值传递❌ 不能const 只影响函数内部,不影响调用时的匹配。
引用传递✅ 能int&const int& 是不同的类型,可以绑定不同的实参。
指针传递✅ 能int*const int* 是不同的类型,可以指向不同的数据。

关于左值,右值另有高论,参考:深入浅出C++左值与右值 —— 一个视频彻底搞懂左值与右值!_哔哩哔哩_bilibili

2,注意默认参数导致的报错

二,类与封装

访问权限相关18
class和struct区别19

默认访问权限,s公有,c私有。

读写控制set/get20
立方体实例21
文件关系22

点和圆关系为例子,将类的声明放在头文件中,将类的实现放在主文件中!

方式优点缺点
#pragma once简洁,不易出错非标准(但几乎所有现代编译器都支持)
#ifndef 宏保护标准 C/C++,兼容性强需要手动确保宏名唯一,代码稍显冗长
初识构造函数与析构函数23

构造函数:

1,名同类名,可以有参数,可以重载,对象实例化时自动调用,编译器会在没有设置构造函数时,自动添加一个空构造函数。

2,只会调用一次。

析构函数:

1,~名同类名,无参无构造,不可重载,对象销毁前(出栈)自动调用。编译器会在没有设置析构函数时,自动添加一个空析构函数。

2,只会调用一次,写在main函数中时,会在main函数结束时销毁。

构造函数的分类和调用24

1,按照有无参数分类:有参构造,无参构造

2,按照参数类型分类:普通构造,拷贝构造

拷贝构造传参为常量类对象的引用,可以拷贝出一个和该对象相同的对象。

构造函数的调用方法:

1,括号法

2,显示法

3,隐式转换法

简单写下代码

class Person{
  private:
	int age;
  public:
    //constructor
    //Parameterless Construction
    Person(){
        age = 0;
    }
    //Parametrical Construction
    Person(int a){
        age = a;
    }
    //Copy Construction
    Person(const Person &p1){
        age = p1.age;
    }
    
};

void test01{
	//bracket call
    Person p;
    Person p1(10);
    Person p2(p);
    //tips:
    (X)Person p1();
    //In this way , the compiler will evaluates "Person p1()" to a func return type Person.
    
    //display call
    Person p3;
    Person p4 = Person(10);
    Person p5 = Person(p4);
    //anonymous object will destroy after this line call.
    Person(12);
    //tips:
    (x)Person(p5);
    //We can't initialize anonymous object in Copy Construction.
    
    //Implicit Conversion
    Person p6;
    Person p7 = 10;
    Person P8 = p7;
}
拷贝构造时机25

1,使用一个创建完成的类对象来初始化一个新对象

2,值传递给函数参数传值

3,类对象做返回值(以值方式返回局部对象)

tesr01{
    Person p1(10);
    Person p2(p1);
}

test02(Person p){
    Person p3(p);
}

Person test03(){
    //
	return Person p;
}

构造函数调用规则26

一个类定义时,系统自动提供三种构造函数。

1,用户自定义有参,系统不自动提供无参

2,用户自定义拷贝,系统不自动提供其他

拷贝构造的浅拷贝与深拷贝27

类默认提供的拷贝构造是浅拷贝,只将类成员变量的值复制给要实例化的对象。在类中有指针成员变量时,浅拷贝只会传递内存地址,在析构函数中会触发堆区内存重复释放的错误,

class Person{
    public:
	int age;
    int *h;
	
    Person(const Person p){
        age = p.age;
        //deep copy
        int h = new int(*p.h);
    }
    
    ~Person(){
        if(h != NULL){
			delete h;
        }
        h = NULL;
    }
};

构造函数初始化列表28
int ca;
int cb;
int cc;

Person(int a,int b,int c) : aa(a),cb(b),cc(c){}
其他类对象做类的成员,构造/析构顺序29

先调用其他类的构造,最后调用其他类的析构

class Phone{
   string pname;
   phone (string name):pname(name){}
} ;

class Person{
    string hname;
    Phone pname;
    //Phone pname = phone
    //Implicit Conversion
    Person(string person, string phone):hname(person),pname(phone){}
}
静态成员函数与静态成员变量30&31

1,静态成员函数和静态成员变量都有访问权限

静态成员变量:

1,所有类对象公用的变量(共用一份数据)

2,在编译阶段分配内存

3,类内声明,类外初始化

对于第三点,我进行了一些尝试,结果是,必须在类外初始化。

如果尝试在定义时初始化,则语法报错提示,如果需要初始化静态成员变量,则该变量必须是常量。

如果只在构造时初始化,则编译后(build error)报错:

LNK2001 unresolved external symbol “public: static int Student::Sch_s”

所以在类外做初始化是必要的!

class Student {
public:
	static int Sch_s ;
	static string Sch_n;
	int Xh = 10000;
	string stu_name = "Lim";
	Student() {
		Sch_s++;
	}

	Student(int xh, string name) :Xh(xh), stu_name(name) {
		Sch_s++;
	}
	
    static void ChangeSchName(string name){
        Sch_n = name;
        cout<<"This School change name to "<<name<<endl;
    }
    
	void show() {
        cout<<"The school name is "<<Sch_n<<endl;
        
		cout << "Name  :" << stu_name << "   XueHao  :" << Xh << endl;
		cout << "Now School has " << Sch_s << "Students" << endl;
	}

};

int Student::Sch_s = 0;
string Student::Sch_n = "XiWang";
int main() {
	Student s1;
	Student s2(10001, "miss");
	s1.show();
    s1.ChangeSchName("GuangMing");
	s2.show();
}

静态成员函数:

1,所有类对象共用一个静态成员函数。

2,静态成员函数只能访问静态成员变量。

静态成员函数不能访问非静态成员变量的原因是:

静态成员函数分不清非静态成员变量到底是哪一个对象的变量。

C++对象特性(模型)和this指针之章

成员变量和成员函数分开存储32

通过sizeof检查大小发现,C++编译器会给每个空对象分配一个字节空间,为了区分空对象占用内存的位置。只有类的非静态成员变量在类的对象上,用sizeof可以检查到空间占用。

也就是说,类的非静态成员函数,类的静态成员变量,类的静态成员函数都不在类的对象上。

this指针用途(链式编程思想)33

this指针用途:

1,解决名称冲突

解决形参列表和类成员变量重名的冲突

2,返回对象本身引用

this指针指向被调用的成员函数所属的对象,可以用于在类的非静态成员函数中返回对象本身(引用方式)。

如果在下面的例子中不使用引用的方式,则每次调用add函数只会返回一个对于篇

的拷贝构造,实际上后续的修改没有修改到p2上,所以最终的值为20。

class Person{
	int age;
    person(int age){
        //age = age;
        this->age = age;
    }
    //Person
    Person& personAddAge(Person &p){
        this->age += p.age;
        return *this;
    }
};

void test01(){
    Person p1(18);
}

void test02(){
    Person p1(10);
    Person p2(10);
    p2.personAddAge(p1).personAddAge(p1).personAddAge(p1);
}
空指针访问类的成员函数34

空类指针(同this指针)可以访问类的成员函数。

类成员函数中使用到类成员变量//时,会自动在前面加上this指针,所以空指针无法取到该成员变量的值会报错。

class Person{
    int m_age;
  	showPersonAge(){
        //cout<<this->age;
        cout<<m_age;
    }  
};
const修饰成员函数35

this补充:

1,this 指针隐含在每一个非静态成员函数之中

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

常函数:

1,成员函数后加const后是常函数

2,常函数内不能修改成员属性

3,成员属性声明时加关键字mutable后,在常函数中可以修改

常对象:

1,声明对象前加const的对象是常对象

2,常对象只能调用常函数

class Person{
    public:
    (x)void theEssenceOfThis(){
        this = NULL;
    }
    //Can't change point to(address) also can't change the point to value
    //const Person * const this
    void constantFunc() const{
        //Variables that are not mutable cannot be modified.
        //this->m_a = 100;
        this->m_b = 100;
    }
    
    int m_a;
    mutable int m_b;
};

void test01(){
    Person p;
    //this is Pointer Constant,Can't change point to
    //this(Person * const this)
    p.theEssenceOfThis();
}

void test02(){
    Person p;
    p.constantFunc();
}

//constant obj
void test03(){
	const Person p;
    //p.m_a = 100;
    p.m_b = 100;
    //constant obj can just call constant func
    p.constantFunc();
}
友元36

1,全局函数做友元,可以访问类的私有部分。

class Building{
    //pass by address , avoid the cost of memory
    friend void frifunc(Building * building);
    public:
   		Building(){
            this->livingroom = "keting";
            this->bedroom = "woshi";
        }
    public:
    	string livingroom;
    private:
    	string bedroom;
};

void frifunc(Building* building){
    cout<<building.livingroom;
    cout<<building.bedroom;
}

int main(){
    Building b;
    frifunc(&b);
    return 0;
}

关于类指针的思考

表达式含义地址类型
l1指针存储的地址(指向堆上的 Liter 对象)堆地址
&l1指针变量 l1 本身的地址(在栈上)栈地址
  • l1 是动态分配的对象的地址(堆)。
  • &l1 是指针变量本身的地址(栈)。
  • delete l1 释放堆内存,但不影响 l1 的栈地址。
#include<iostream>
using namespace std;

class Liter {
public:
	char a, b, c;
	Liter(char a, char b, char c) {
		this->a = a;
		this->b = b;
		this->c = c;
	}
};

int main() {
	Liter *l1 = new Liter('a','b','c');
	cout << l1 << endl;
	cout << &l1 << endl;
	//000002143D4A7EC0
	//0000009942EFFB18
	delete l1;
	l1 = nullptr;
	return 0;
}
友元类37

注意,友元关系是一种 “访问授权”,而不是 “继承授权”。所以没有继承友元类这样的说法。

类做友元,可以访问类的私有部分。

class Friend{
public:
    Friend();
    void visit();
    Building * building;
}


class Building{
    
    
    //
    friend class Friend;
    
    //pass by address , avoid the cost of memory
    friend void frifunc(Building * building);
    public:
   		Building(){
            this->livingroom = "keting";
            this->bedroom = "woshi";
        }
    public:
    	string livingroom;
    private:
    	string bedroom;
};

//member func realize out of class

Friend::Friend(){
    building = new Building;
}

void Friend::visit(){
    building->livingroom = "ke";
    building->bedroom = "wo";
}


成员函数做友元38
class Building{
  	friend void Friend::visit();  
    
};

运算符重载之章

加法运算符重载39

1,成员函数重载

2,全局函数重载

运算符重载主要用于自定义类型运算,也可以使用函数重载。

class Person{
   public:
   int m_a;
   int m_b;
   
   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;
   }
   
   Person operator+ (Person &p ,int num){
       Person temp;
       temp.m_a = this -> m_a + num;
       temp.m_b = this -> m_b + num;
       return temp;
   }
}

Person operator+ (Person &p1, Person &p2){
   Person temp;
   temp.m_a = p1 -> m_a + p2.m_a;
   temp.m_b = p2 -> m_b + p1.m_b;
   return temp;
}

void test01(){
   Person p1;
   p1.m_a = 10;
   p1.m_b = 10;
   Person p2;
   p2.m_a = 10;
   p2.m_b = 10;
   
   Person p3 = p1 + p2;
   Person p4 = p1+100;
}
左移运算符重载40

不用成员函数做左移运算符重载的原因是格式不好看(无法实现cout在左侧),用去全局函数做重载,传入ostream对象cout,和类对象,再应用一些链式编程思想,即可完成连续输出。

class Person{
public:
    int age;
    string name;
    //p.operator<<(p)
    (x)Person operator<< (Person &p);
    //p << cout
    (x)Person operator<< (ostream &cout);
}
//standard out stream object only one in whole situation
ostream & operator<< (ostream & cout , Person & p){
    cout<<"age : "<<p->age<<" Name : "<< o->name <<endl;
    return cout;
}
自增/自减运算符重载41

要是没有黑马的教程我根本就注意不到这二者的区别:

int a = 10;
--(--a);
(x)(a--)--;
表达式返回类型是否合法原因
--(--a)左值合法可以对左值连续递减
(a--)--右值非法不能对右值递减
  • 核心区别:前置递减返回左值,后置递减返回右值。
  • 设计逻辑:后置递减需要返回原始值(临时值),因此不能支持连续递减。
class Czpp {
	friend ostream& operator<< (ostream& cout, Czpp c01);
public:
	Czpp() {
		o = 0;
	}
	//the reason of add & becaues ++op is design for continuous output.
	//So we can call ++(++a) trustingly
	Czpp&  operator++ () {
		o++;
		return *this;
	}

	Czpp operator++ (int) {
		Czpp temp = *this;
		o++;
		return temp;
	}

private:
	int o;
};

ostream& operator<< (ostream& cout, Czpp c01) {
	cout << c01.o << endl;
	return cout;
}

void testcase(){
	Czpp c01;
	cout << ++(++c01) << endl;
	cout << c01 << endl;
	cout << ((c01++)++)++ << endl;
	cout << c01 << endl;
}


class Czmm {

	friend  ostream& operator<< (ostream& cout, Czmm c01);

public:
	Czmm() {
		oi = 100;
	}

	Czmm(int n) {
		oi = n;
	}

	Czmm & operator-- () {
		oi
			--;
		return *this;
	}

	Czmm operator--(int) {
		Czmm temp = *this;
		oi--;
		return temp;
	}
private:
	int oi;
};


ostream& operator<< (ostream& cout, Czmm c01) {
	cout << c01.oi << endl;
	return cout;
}

void test02() {
	Czmm c02;
	cout << --c02 << endl;
	cout << --(--c02) << endl;
	Czmm c03(20);
	cout << c03-- << endl;
	cout << c03 << endl;
}
赋值运算符重载42

C++编译器会给一个类添加四个函数:

1,默认构造函数,函数体为空。

2,默认析构函数,函数体为空。

3,默认拷贝构造函数,对属性进行值拷贝。

4,赋值运算符 operator= 对属性进行值拷贝。

我们在类外进行类的等号赋值操作时,进行的是浅拷贝,容易触发地址重复释放问题。可以通过重载赋值运算符进行深拷贝规避问题。

class Person{
public:
	int* m_age;
    
    Person(int age){
        m_age = new int(age);
    }
    
    
    //add operator= heavyload
    
    //chain programming ideas
    Person & operator= (Person& p){
        //Compiler offer shallow copy
        //m_age = p.m_age;
        
        //Defensive programming
        //Chenking if this property is in heap area
        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;
        }
    }
};

void test01(){
    Person p1(18);
    Person p2(20);
    
    //assignment operatation will trigger shallow copy
    //Repetition release heap area memory
    p1 = p2;
    
    cout<<*p1.m_age<<endl;
    cout<<*p2.m_age<<endl;
    
}
关系运算符重载43

涉及>,<,==,!=等,返回bool类型值。

函数调用重载运算符&匿名函数对象44

函数调用()也可以重载

class Myprint{
  	public:
    void operator()(string rest){
        cout<<rest<<endl;
    }
};

void test01(){
    Myprint mp;
    mp("hello world");
    //anonymous func object
    Myprint()("hello world");
}

继承之章

初识继承45

公共部分抽象为父类,子类继承

继承方式46

在这里插入图片描述

继承中的对象模型47

子类继承父类,会得到所有父类成员变量,只是会自动隐藏父类的私有成员变量。通过vs Developer Command Prompt查看(报告单个类的布局)

cl /d1 reportSingleClassLayout类名 “cpp文件名”
继承中的构造以及析构顺序48

父类先构造,后析构

同名成员处理49

如果子类中出现和父类同名的成员函数,子类的同名成员函数会自动隐藏掉父类中所有的同名成员函数,在有子类的情况下,(即使有能明显区分的重载也不行)访问父类中被隐藏的同名成员函数,需要加作用域。

同名静态成员处理50

1,通过对象访问

2,通过类名访问

class father {
public:
	int fapuint;
	static int fapusint;
protected:
	int faprint;
	static int faprsint;
private:
	int fapriint;
	static int faprisint;

public:

	father() {
		fapuint = 10;
		//I forget how to init static ^-^
		/*fapusint = 100;*/
		faprint = 20;
		fapriint = 30;
	}

	void pint() {
		cout << "father func" << endl;
	}

	static void spint() {
		cout << "father static func" << endl;
	}

};

int father::fapusint = 100;

class son : public father {

public:
	static int fapusint;

	void pint() {
		cout << "son func" << endl;
	}

	static void spint() {
		cout << "son static func" << endl;
	}
};

int son::fapusint = 100;

void tcinher() {
	son s1;
	//access from obj 
	cout << "son ' s varible  access with obj" << s1.fapuint << endl;
	cout << "fa ' s varible  access with obj" << s1.father::fapuint << endl;
	//access from action scope(class name)
	//This access way is only ok to static varible / func.
	cout << "son ' s static varible  access with class name" << son::fapusint << endl;
	cout << "fa ' s static varible  access with class name" << son::father::fapusint << endl;

	//son ' s func access with obj
	s1.pint();
	//fa ' s func access with obj
	s1.father::pint();
	//son ' s static func  access with class name
	son::spint();
	//fa ' s static func  access with class name
	son::father:: spint();
}
继承语法51

多继承,注意以作用域区分同名。

class son : public base1, public base2{
    
};
菱形继承52

虚基类,虚继承

情况继承方式grandfa 子对象数量访问 m_age 是否二义
普通继承class fath : public grandfa2 份(fath + math是(需指定路径)
虚继承class fath : virtual public grandfa1 份(共享)否(可直接访问)
  • fathmath 共享同一个 grandfa 子对象
  • sons 只包含 一个 grandfa 实例,避免了二义性。
底层实现(编译器如何支持虚继承?)

虚继承的实现通常依赖 虚基类表(Virtual Base Table, vbtable)

  1. 虚基类指针(vbptr)
    • 当某个类虚继承自另一个类时,编译器会为该类添加一个 虚基类指针(vbptr),指向虚基类表(vbtable)。
  2. 虚基类表(vbtable)
    • 存储了虚基类相对于当前对象的偏移量。
    • 这样,即使 fathmathsons 中的位置不同,它们也能正确找到共享的 grandfa 子对象。
class grandfa {
public:
	int m_age;

};
//class uncle : virtual public grandfa
class uncle : public grandfa {
public:

};
//class math : virtual public grandfa
class math : public grandfa {
public:

};
//class fath : virtual public grandfa
class fath :  public grandfa {
public:

};

class sons :public fath, public math , public uncle {
public:

};

void testv() {
	sons son1;
	
	son1.uncle::m_age = 77;
	son1.math::m_age = 19;
	/*son1.fath::m_age = 20;*/
	son1.grandfa::m_age = 100;

	//cout << "son's age = " <<son1.m_age << endl;
	cout << "uncle's age = " << son1.uncle::m_age << endl; // 77(未被修改)
	/*cout << "fath's age = " << son1.fath::m_age << endl;*/
	cout << "math's age = " << son1.math::m_age << endl;   // 19(未被修改)
	cout << "gf's age = " << son1.grandfa::m_age << endl;  // 100(修改的是 fath 的版本)
	
}

实际上我在看到多态原理剖析时,用sizeof测试了各个类的大小:

cout << "size of gf " << sizeof(grandfa) << endl;//4
cout << "size of uncle " << sizeof(uncle) << endl;//16
cout << "size of math " << sizeof(math) << endl;//16
cout << "size of fath " << sizeof(fath) << endl;//16
cout << "size of sons " << sizeof(sons) << endl;//32

Deepseek声称可能存在内存对齐,虚基类指针占用8位,对齐4位,继承的int4位。

在不使用虚继承时出现的情况:

  1. son1.grandfa::m_age = 100;模糊访问,但某些编译器(如 MSVC)会 隐式选择第一个基类(fath)的 grandfa,因此:
    • 它修改的是 fathgrandfa::m_age(但 fath::m_age 之前未被赋值,可能是随机值或 0)。
    • uncle::m_agemath::m_age 未被影响,仍然是 7719
  2. 输出时:
    • son1.uncle::m_age77(未被 grandfa::m_age = 100 影响)。
    • son1.math::m_age19(未被影响)。
    • son1.grandfa::m_age100(修改的是 fath 的版本)。

问题:为什么 grandfa::m_age = 100 不影响 uncle::m_agemath::m_age

因为:

  • sons 有 3 份独立的 grandfa 子对象fathmathuncle 各一份)。
  • son1.grandfa::m_age 只能修改其中一份(具体是哪份取决于编译器,这里是 fath 的)。
  • uncle::m_agemath::m_age 是另外两份,不受影响。
EX:继承中的父子类思考:
为什么子类构造时要求父类必须有默认构造函数?

在 C++ 中,子类对象的构造过程一定会先构造父类部分。如果父类没有默认构造函数(无参构造函数),而子类的构造函数又没有显式调用父类的某个构造函数,编译器就无法自动构造父类,从而导致编译错误。


1. 构造顺序:父类 → 子类

当创建一个子类对象时,构造顺序是:

  1. 父类的构造函数(如果父类还有父类,则继续向上递归)
  2. 子类成员变量的构造函数(按声明顺序)
  3. 子类自己的构造函数

如果父类没有默认构造函数,而子类又没有显式指定调用父类的哪个构造函数,编译器就不知道如何构造父类部分,因此会报错。


2. 示例分析
❌ 错误情况:父类没有默认构造函数,子类未显式调用父类构造函数
class Parent {
public:
    Parent(int x) { }  // 只有带参数的构造函数,没有默认构造函数
};

class Child : public Parent {
public:
    Child() { }  // 错误!编译器不知道如何构造 Parent
};

int main() {
    Child c;  // 编译失败
    return 0;
}

报错信息:

error: no matching function for call to 'Parent::Parent()'

原因:
Child() 构造函数没有显式指明如何构造 Parent,编译器尝试调用 Parent::Parent()(默认构造函数),但 Parent 没有默认构造函数,因此报错。


✅ 正确做法:
(1)父类提供默认构造函数
class Parent {
public:
    Parent() { }       // 提供默认构造函数
    Parent(int x) { }  // 也可以有其他构造函数
};

class Child : public Parent {
public:
    Child() { }  // 现在可以编译,自动调用 Parent()
};
(2)子类显式调用父类的某个构造函数
class Parent {
public:
    Parent(int x) { }  // 只有带参数的构造函数
};

class Child : public Parent {
public:
    Child() : Parent(42) { }  // 显式调用 Parent(int)
};

int main() {
    Child c;  // 正确,Parent 被构造为 Parent(42)
    return 0;
}

关键点:

  • Child() : Parent(42)成员初始化列表(Member Initializer List),在这里显式调用了 Parent(int)
  • 如果不写 : Parent(42),编译器会尝试调用 Parent(),但 Parent 没有默认构造函数,导致编译错误。

3. 更深层次的情况:多重继承

如果子类继承多个父类,且某些父类没有默认构造函数,必须在子类构造函数中显式调用它们的构造函数:

class Parent1 {
public:
    Parent1(int x) { }
};

class Parent2 {
public:
    Parent2(int y) { }
};

class Child : public Parent1, public Parent2 {
public:
    Child() : Parent1(10), Parent2(20) { }  // 必须显式调用
};

如果不写 : Parent1(10), Parent2(20),编译器会报错!


4. 为什么 C++ 这样设计?
  • 明确性:强制程序员显式指定如何构造父类,避免意外行为。
  • 灵活性:允许父类的构造依赖子类传递的参数。
  • 避免二义性:如果父类有多个构造函数,编译器不知道默认用哪个,必须由程序员指定。

总结
情况父类构造函数子类构造函数写法是否合法
父类有默认构造函数Parent() 存在Child() { }✅ 合法
父类无默认构造函数只有 Parent(int)Child() { }❌ 编译错误
父类无默认构造函数只有 Parent(int)Child() : Parent(42) { }✅ 合法

关键规则:

  • 如果父类没有默认构造函数,子类必须显式调用父类的某个构造函数
  • 如果父类有默认构造函数,子类可以不显式调用(编译器自动调用 Parent())。
多态的基本语法53

1,静态多态:

地址早绑定,编译阶段确定函数地址。例如函数重载,运算符重载都属于静态多态,复用函数名。

2,动态多态:

地址晚绑定,运行阶段确定函数地址。用派生类和虚函数实现运行时多态。

虚函数:子类重写父类函数(返回值,参数列表,函数名相同),调用时父类对象的引用作为形参,在父类被重写函数前加上virtual关键字,使之成为虚函数,则可以根据传入参数的对象类型选择对应类的函数调用。

  • 虚函数:使得基类指针或引用指向派生类对象时,可以调用派生类的重写函数,实现多态。
  • 动态联编(Dynamic Binding):指的是程序运行时,系统动态决定调用哪个函数,常常与虚函数一起使用。
  • 滞后联编(Late Binding):与动态联编是同义的,强调函数调用的决定是在程序运行时,而不是编译时。

两者本质上是相同的,滞后联编更多是对“运行时选择函数”过程的描述,而动态联编则是虚函数机制的一部分,指代了在运行时基于对象的实际类型来选择函数的调用。

子类重写函数前virtual可加可不加。

class animals {
public:
	virtual void speak() {
		cout << "anilmals' speak" << endl;
	}
};

class cats : public  animals{
public:
	void speak() {
		cout << "cats' speak" << endl;
	}
};

class dogs : public  animals {
public:
	void speak() {
		cout << "dogs' speak" << endl;
	}
};

void dospeak(animals & animal) {
	animal.speak();
}

void tc01() {
	animals ani;
	cats c01;
	dogs d01;
	dospeak(ani);
	dospeak(c01);
	dospeak(d01);
}
多态的原理剖析54

使用virtual关键字修饰父类函数后,父类中存储一个虚函数指针(vfptr) 指向虚函数表(vftable),当子类重写出现时,子类的虚函数指针指向子类的重写后的虚函数。sizeof验证都为8

cout << "size of anis " << sizeof(animals) << endl;
cout << "size of ani " << sizeof(ani) << endl;
cout << "size of cat " << sizeof(cats) << endl;
cout << "size of c01 " << sizeof(c01) << endl;
cout << "size of dog " << sizeof(dogs) << endl;
cout << "size of d01 " << sizeof(d01) << endl;
多态案例之计算器55

注意,多态的应用方式有两种:父类的引用/指针指向子类对象

利用多态的特性,我们可以在扩展程序功能时,尽可能不修改源码,也就是“开放扩展,关闭修改”。

多态的代码组织下,结构清晰,代码可读性高。多态的应用方法为父类的指针/引用指向子类对象。

class calculator {
public:
	int num_1;
	int num_2;

	int getRes(string oper) {
		if (oper == "+") {
			return num_1 + num_2;
		}
		else if (oper == "-") {
			return num_1 - num_2;
		}
	}
};

class abstractCalc {
public:
	int num_1;
	int num_2;

	virtual int getRes() {
		return 0;
	}
};

class addcalc : public abstractCalc {
public:
	int getRes() {
		return num_1 + num_2;
	}
};

class subcalc : public abstractCalc {
public:
	int getRes() {
		return num_1 - num_2;
	}
};

void test() {
	calculator c;
	c.num_1 = 10;
	c.num_2 = 10;
	cout << c.getRes("+") << endl;
	cout << c.getRes("-") << endl;


	//Method of application of polymorphic
	//父类指针/引用指向子类对象

	abstractCalc *ac = new addcalc;
	ac->num_1 = 10;
	ac->num_2 = 10;
	cout << ac->getRes() << endl;

	delete ac;

	abstractCalc* ac2 = new subcalc;
	ac2->num_1 = 10;
	ac2->num_2 = 10;
	cout << ac2->getRes() << endl;
	delete ac2;

}
纯虚函数和抽象类56

之前coding的时候我就有这样的疑问,实例化父类和子类的关系,在这里有了答案。我们可以将父类成员函数变为纯虚函数,将父类变为抽象类,这样父类无法实例化。子类必须重写父类的纯虚函数,否则也会变成纯虚函数。

class animals{
  	public:
    int age;
    virtual void name() = 0;
};

class dogs {
    public:
    void name(){
        age = 10;
    }
}
制作饮品57
class make{
  public:
    virtual procedure01() = 0;
    virtual procedure02() = 0;
    virtual procedure03() = 0;
    virtual procedure04() = 0;
};

class teamake: public make{
  public:
    procedure01(){
        cout<<"tea01"<<endl;
    }
    procedure02(){
        cout<<"tea02"<<endl;
    }
    procedure03(){
        cout<<"tea03"<<endl;
    }
    procedure04(){
        cout<<"tea04"<<endl;
    }
};

void test(){
    teamake tm;
    tm.procedure01();
}
虚析构函数和纯虚析构函数58

和虚函数,纯虚函数类似,虚析构可以让父类指针释放子类对象(根据子类的析构函数),纯虚析构需要在类外初始化,并且一旦定义纯虚析构,该类即为抽象类,无法实例化对象。

class Animals{
    public:
    int age;
    virtual ~Animals() = 0;
};

Animals::~Animals(){
    cout<<"pure virtual func call"<<endl;
}

class dogs{
  public:
    string* dname;
    ~dogs(){
        if(dname != NULL){
			delete dname;
            dname = NULL;
        }
    }
}
多态案例组装电脑59/60
//polymorphic practice

class CPU {
public:
	virtual void cpuwork() = 0;
};

class GPU {
public:
	virtual void gpuwork() = 0;
};

class computers {
public:

	

	void dowork() {
		m_cpu->cpuwork();
		m_gpu->gpuwork();
	}

	computers(CPU* cpu, GPU* gpu) : m_cpu(cpu),m_gpu(gpu) {}

	~computers() {
		if (m_cpu != NULL) {
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_gpu != NULL) {
			delete m_gpu;
			m_gpu = NULL;
		}
	}

private:
	CPU* m_cpu;
	GPU* m_gpu;
};

class intelCPU : public CPU {
public:
	void cpuwork() {
		cout << "InterCPU is working" << endl;
	}
};

class AMDCPU : public CPU {
public:
	void cpuwork() {
		cout << "AMDCPU is working" << endl;
	}
};

class AMDGPU : public GPU {
public:
	void gpuwork() {
		cout << "AMDGPU is working" << endl;
	}
};

class NvidaGPU : public GPU {
public:
	void gpuwork() {
		cout << "NvidaGPU is working" << endl;
	}
};

void polytest() {
	intelCPU* intelc01 = new intelCPU;
	AMDCPU* AMDc01 = new AMDCPU;
	AMDGPU* AMDg01 = new AMDGPU;
	NvidaGPU* Ng01 = new NvidaGPU;

	CPU* intelc = new intelCPU;
	CPU* AMDc = new AMDCPU;
	GPU* AMDg = new AMDGPU;
	GPU* Ng = new NvidaGPU;


	computers * com01= new computers(AMDc01, AMDg01);
	computers* com02 = new computers(intelc, AMDg);
	com01->dowork();
	com02->dowork();
	delete com01;
	delete com02;

	computers* com03 = new computers(new intelCPU, new NvidaGPU);
	com03->dowork();
	delete com03;
}
ChatGPT评价道:

polytest() 中,com01com02 使用了不同的方式来创建 computers 对象,其区别主要体现在多态性内存管理两个方面。

1. 父类指针指向子类对象(com02)
CPU* intelc = new intelCPU;
GPU* AMDg = new AMDGPU;
computers* com02 = new computers(intelc, AMDg);

特点:

  • 使用 父类指针指向子类对象,体现了多态性
  • computers 中的 m_cpum_gpu 被声明为 CPU*GPU*,因此传入父类指针符合多态的设计。
  • 在实际运行时,通过虚函数表(vtable)实现动态绑定,会执行 intelCPU::cpuwork()AMDGPU::gpuwork()
  • 推荐这种方式,符合面向对象的多态原则,使代码更加灵活。

内存管理注意:

  • delete com02; 时,会执行 computers 的析构函数,调用 delete m_cpudelete m_gpu
  • 父类指针删除子类对象时,必须确保基类有虚析构函数,否则可能会造成内存泄漏或未完全析构子类对象。

2. 子类指针指向子类对象(com01)
AMDCPU* AMDc01 = new AMDCPU;
AMDGPU* AMDg01 = new AMDGPU;
computers* com01 = new computers(AMDc01, AMDg01);

特点:

  • 使用 子类指针指向子类对象,在对象传入 computers 的构造函数时,会发生隐式向上转型
  • 这种方式虽然也能实现多态行为,但丧失了父类指针的灵活性。
  • 在程序中不太推荐直接使用子类指针来管理对象,因为它不具备多态的优势。

内存管理注意:

  • computers 析构函数中依然会调用 delete 删除对象。虽然子类对象会正确析构,但这种管理方式容易导致代码的可维护性下降。
  • 如果后期需要更换不同的 CPUGPU,这种写法的灵活性较差。

总结
区别父类指针指向子类对象 (com02)子类指针指向子类对象 (com01)
多态性支持多态,运行时动态绑定不支持多态,失去灵活性
内存管理安全性较高,方便统一管理容易出错,不便于维护
扩展性便于扩展其他CPU/GPU类扩展性差,不利于维护
推荐程度✅ 推荐使用❌ 不推荐

因此,在实际开发中,父类指针指向子类对象是更优的选择。

C++写/读文本文件61/62

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放,通过文件可以将数据持久化。C++中对文件读写需要包含头文件

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

void writet() {
	ofstream ofs;
	ofs.open("test.txt", ios::out);

	ofs << "名称:张三" << endl;
	ofs << "年龄:18" << endl;
	ofs<< "性别:男"<<endl;
	ofs.close();
	return;
}

void readt() {
	ifstream ifs1;
	ifstream ifs2;
	ifstream ifs3;
	ifstream ifs4;

	ifs1.open("test.txt", ios::in);
	ifs2.open("test.txt", ios::in);
	ifs3.open("test.txt", ios::in);
	ifs4.open("test.txt", ios::in);

	/*if (!ifs.is_open()) {
		cout << "The file open filled" << endl;
	}*/

	//method 1
	char buf[1024];
	while (ifs1 >> buf) {
		cout << buf << endl;
	}

	cout << endl;

	//method 2
	char buf2[1024];
	while (ifs2.getline(buf2, sizeof(buf2))) {
		cout << buf2 << endl;
	}

	cout << endl;

	//method 3
	string buf3;
	while (getline(ifs3, buf3)) {
		cout << buf3 << endl;
	}

	cout << endl;

	//method 4
	char buf4;
	while ((buf4 = ifs4.get())!=EOF ) {
		cout << buf4;
	}

	ifs1.close();
	ifs2.close();
	ifs3.close();
	ifs4.close();
}

int main() {
	writet();
	readt();

	return 0;
}

读取方法1遇到空格会自动换行。

C++写/读二进制文件63/64
#include<iostream>
#include<fstream>
using namespace std;

class Person {
public:
	int m_age;
	char m_name[64];
};

void test() {

	ofstream ofs("Person.txt", ios::out | ios::binary);
	Person p1 = { 89, "张三" };
	ofs.write((const char*)&p1, sizeof(Person));
	ofs.close();

	ifstream ifs("Person.txt", ios::in | ios::binary);
	Person p;
	ifs.read((char*)&p, sizeof(Person));
	cout << p.m_age << endl<<p.m_name;
	ifs.close();
}

int main() {
	test();
}

注意事项:

1,sizeof(Person) / sizeof§ / sizeof(p1) 没有区别,这里在类中定义了char的大小为64字节。

2,在写文件时,我们不希望p数据被修改。write 用于写入数据到文件,它不会修改传入的数据,所以参数类型是 const char*(表示“只读”)。在读文件时,read 用于从文件读取数据,它会修改传入的缓冲区(写入数据到 s),所以参数类型是 char*(表示“可修改”)。

实现职工管理系统

EMS.h(EmployerManagerSystem)

#pragma once
#include<iostream>
using namespace std;
#include "worker.h"
#include<fstream>

#define FILENAME "Worker.txt"

class EmployerManager {
public:
	bool m_fileIsEmpty;

	int m_workerNum;

	Worker** m_workerArray;

	EmployerManager();

	void add_worker();

	void init_workerArray();

	int get_workerNum();

	void show_worker();

	void delete_worker();

	void modify_worker();

	void find_worker();

	int isExist(int id);

	void showMenu();

	void sort_worker();

	void clean_file();

	void exitSystem();

	void save();

	~EmployerManager() ;
};

worker.h

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

class Worker {
public:
	virtual void showInfo() = 0;

	virtual string getDeName() = 0;

	int m_Id; //职工编号
	string m_Name;
	int m_DeId; //部门编号
	/*string m_DeName;*/

};

employee.h

#pragma once
#include<iostream>
using  namespace std;

#include "worker.h"

class Employee : public Worker {

public:
	Employee(int id, string name, int dId);

	virtual void showInfo();

	virtual string getDeName();
};

manager.h

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

class Manager : public Worker {
public:
	Manager(int id, string name, int dId);

	virtual void showInfo();

	virtual string getDeName();

};

boss.h

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

class Boss : public Worker {
public:
	Boss(int id, string name, int dId);

	virtual void showInfo();

	virtual string getDeName();

};

EMS.cpp

#include "EMS.h"
#include<fstream>
#include "employee.h"
#include"manager.h"
#include"boss.h"

int getValidInt(const string& prompt) {
	int value;
	while (true) {
		cout << prompt << endl;
		cin >> value;

		if (cin.fail()) { // 检测输入是否失败
			cin.clear(); // 清除错误标志
			cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 丢弃错误输入
			cout << "Invalid input! Please enter a number." << endl;
		}
		else {
			cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 清除缓冲区
			return value;
		}
	}
}

string getValidString(const string& prompt) {
	string value;
	cout << prompt << endl;
	cin >> value;
	return value;
}


EmployerManager::EmployerManager() {

	ifstream ifs;
	ifs.open(FILENAME, ios::in);

	if (!ifs.is_open()) {
		cout << "FILE is not Exist!" << endl;
		this->m_workerNum = 0;
		this->m_fileIsEmpty = true;
		this->m_workerArray = NULL;
		ifs.close();
		return;
	}

	char ch;
	ifs >> ch;
	if (ifs.eof()) {
		cout << "FILE is empty!" << endl;
		this->m_workerNum = 0;
		this->m_fileIsEmpty = true;
		this->m_workerArray = NULL;
		ifs.close();
		return;
	}

	int num = this->get_workerNum();
	cout << "Worker Count: " << num << endl;

	this->m_workerNum = num;


	this->m_workerArray = new Worker * [this->m_workerNum];

	init_workerArray();

	for (int i = 0; i < m_workerNum; i++) {
		cout << "Worker serial number : " << this->m_workerArray[i]->m_Id
			<< "\tWorker name : " << this->m_workerArray[i]->m_Name
			<< "\tWorker Department ID : " << this->m_workerArray[i]->m_DeId << endl;
	}
}

void EmployerManager::showMenu() {
	cout << R"(
职工管理系统已启动!
0,退出程序
1,增加职工
2,显示职工
3,删除职工
4,修改职工
5,查找职工
6,职工排序
7,清空数据
			)" << endl;
}

void EmployerManager::add_worker() {
	cout << "Put in the number of worker you want add" << endl;

	int addNum = 0;
	cin >> addNum;

	if (addNum > 0) {
		int newSize = this->m_workerNum + addNum;

		Worker** newSpace = new Worker * [newSize];

		if (this->m_workerArray != NULL) {
			for (int i = 0; i < this->m_workerNum; i++) {
				newSpace[i] = this->m_workerArray[i];
			}
		}


		for (int i = 0; i < addNum; i++) {

			int id = getValidInt("Please put in the " + to_string(i + 1) + " worker serial number");

			string name = getValidString("Please put in the " + to_string(i + 1) + " worker name");

			int dId;

			/*cout << "Please put in the " << i + 1 << " worker serial number" << endl;
			cin >> id;*/

			/*cout << "Please put in the " << i + 1 << " worker name" << endl;
			cin >> name;*/

			cout << R"(Please choose the worker's job
1,Employee
2,Manager
3,Boss)" << endl;

			cin >> dId;

			Worker* worker = NULL;

			switch (dId) {
			case 1:
				worker = new Employee(id, name, dId);
				break;
			case 2:
				worker = new Manager(id, name, dId);
				break;
			case 3:
				worker = new Boss(id, name, dId);
				break;
			default:
				cout << "Invalid choice! Please enter 1, 2, or 3." << endl;
				break;
			}
			//i
			newSpace[this->m_workerNum + i] = worker;

			cout << "Worker " << id << " (" << name << ") added successfully with job ID: " << dId << endl;
		}

		delete[] this->m_workerArray;

		this->m_workerArray = newSpace;

		this->m_workerNum = newSize;

		//Update File is not empty
		this->m_fileIsEmpty = false;

		cout << "Add Succeed " << addNum << " new worker" << endl;

		this->save();
	}
	else {

		cout << "Error put in" << endl;
	}


	//清屏,回到上级目录
	system("pause");
	system("cls");
}

void EmployerManager::init_workerArray() {
	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_workerArray[index] = worker;
		index++;
	}

}

int EmployerManager::get_workerNum() {
	ifstream ifs;
	ifs.open(FILENAME, ios::in);

	int id;
	string name;
	int dId;

	int num = 0;

	while (ifs >> id && ifs >> name && ifs >> dId) {
		num++;
	}

	ifs.close();

	return num;
}

void EmployerManager::save() {
	ofstream ofs;
	ofs.open(FILENAME, ios::out);

	for (int i = 0; i < this->m_workerNum; i++) {

		ofs << this->m_workerArray[i]->m_Id <<" "
			<< this->m_workerArray[i]->m_Name <<" "
			<< this->m_workerArray[i]->m_DeId << endl;
	}

	ofs.close();

}

void EmployerManager::show_worker() {
	if (this->m_fileIsEmpty) {
		cout << "File is not exist or is empty !" << endl;
	}

	else {
		for (int i = 0; i < m_workerNum; i++) {
			this->m_workerArray[i]->showInfo();
		}
	}

	system("pause");
	system("cls");
}

int EmployerManager::isExist(int id) {
	int index = -1;

	for (int i = 0; i < this->m_workerNum; i++) {
		if (this->m_workerArray[i]->m_Id == id) {
			index = i;

			break;
		}
	}

	return index;
}

void EmployerManager::delete_worker() {

	if (this->m_fileIsEmpty) {
		cout << "File is not exist or is empty" << endl;
	}
	else {
		cout << "Put in the ID you want to delete" << endl;
		int id = 0;
		cin >> id;

		int index = this->isExist(id);

		if (index != -1) {
			for (int i = index; i < this->m_workerNum - 1; i++) {
				this->m_workerArray[i] = this->m_workerArray[i + 1];
			}
			this->m_workerNum--;

			this->save();
			cout << "Delete Succeed" << endl;
		}
		else {
			cout << "Delete failed , can't find the id" << endl;
		}
	}

	system("pause");
	system("cls");
}

void EmployerManager::exitSystem() {
	cout << "Welcome use next time" << endl;
	system("pause");
	exit(0);
}

void EmployerManager::modify_worker() {
	if (this->m_fileIsEmpty) {
		cout << "File is not exist or is empty" << endl;
	}
	else {
		cout << "Put in the ID you want to modify" << endl;
		int id;
		cin >> id;

		int ret = this->isExist(id);
		if (ret != -1) {
			delete this->m_workerArray[ret];

			int newId = 0;
			string newName = "";
			int newdId = 0;

			cout << "Find id: " << id << " worker,Please put in newId" << endl;
			cin >> newId;

			cout << "Please put in newName" << endl;
			cin >> newName;

			cout << R"(Please choose the worker's job
1,Employee
2,Manager
3,Boss)" << endl;

			Worker* worker = NULL;

			cin >> newdId;
			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;
			default:
				cout << "Invalid choice! Please enter 1, 2, or 3." << endl;
				break;
			}

			this->m_workerArray[ret] = worker;
			cout << "Modify succeed " << this->m_workerArray[ret]->m_DeId << endl;

			this->save();
		}
		else {
			cout << "Modify Failed , There is no such man" << endl;

		}
	}
	system("pause");
	system("cls");
}

void EmployerManager::find_worker() {
	if (this->m_fileIsEmpty) {
		cout << "File is not exist or is empty" << endl;
	}
	else {
		cout << "Please put in the way to look up" << endl;
		cout << R"(1,Look up from id
2,Look up from name)" << endl;

		int select;
		cin >> select;

		if (select == 1) {
			int id;
			cout << "Please put in the worker id want find" << endl;
			cin >> id;

			int ret = isExist(id);
			if (ret != -1) {
				cout << "Find succeed! The worker info :" << endl;
				this->m_workerArray[ret]->showInfo();
			}
			else {
				cout << "Find failed! No man is this id ." << endl;
			}
		}
		else if (select == 2) {
			string name;
			cout << "Please put in the worker name want find" << endl;
			cin >> name;

			bool flag = false;
			for (int i = 0; i < m_workerNum; i++) {
				if (m_workerArray[i]->m_Name == name) {
					cout << "Find succeed, worker id is: "
						<< m_workerArray[i]->m_Id
						<< "The detail infomation is : " << endl;

					flag == true;

					this->m_workerArray[i]->showInfo();
				}
			}
			if (flag == false) {
				cout << "Find failed! No man is this name ." << endl;
			}
		}
		else {
			cout << "Put in error!" << endl;
		}
	}

	system("pause");
	system("cls");

}

void EmployerManager::sort_worker() {
	if (this->m_fileIsEmpty) {
		cout << "File is not exist or is empty" << endl;

		system("pause");
		system("cls");
	}
	else {
		cout << "Please choose sort way :" << endl;
		cout << R"(1,id ascending order
2,id descending order
)" << endl;
		
		int select = 0;
		cin >> select;

		for (int i = 0; i < m_workerNum; i++) {
			int minOrMaxId = i;
			for (int j = i + 1; j < this->m_workerNum; j++) {
				if (select == 1) {
					if (m_workerArray[minOrMaxId]->m_Id > m_workerArray[j]->m_Id) {
						minOrMaxId = j;
					}
				}

				else {
					if (m_workerArray[minOrMaxId]->m_Id < m_workerArray[j]->m_Id) {
						minOrMaxId = j;
					}
				}
			}

			if (i != minOrMaxId) {
					Worker* temp = m_workerArray[i];
					m_workerArray[i] = m_workerArray[minOrMaxId];
					m_workerArray[minOrMaxId] = temp;
				}
			}

		cout << "Sort succeed, sorted result is :" << endl;
		this->save();
		this->show_worker();
	}
}

void EmployerManager::clean_file() {
	cout << "Confirm clear? " << endl;
	cout << "1,Yes" << endl;
	cout << "2,No" << endl;

	int select = 0;
	cin >> select;

	if (select == 1) {
		ofstream ofs(FILENAME, ios::trunc);
		ofs.close();

		if (this->m_workerArray != NULL) {
			for (int i = 0; i < this->m_workerNum; i++) {
				if (this->m_workerArray[i] != NULL) {
					delete this->m_workerArray[i];
				}
			}
			this->m_workerNum = 0;
			delete[] this->m_workerArray;
			this->m_workerArray = NULL;
			this->m_fileIsEmpty = true;
		}
		cout << "Clean succeed!" << endl;
	}

	system("pause");
	system("cls");
}

EmployerManager::~EmployerManager() {

	if (this->m_workerArray != NULL) {
		delete[] this->m_workerArray;
		this->m_workerArray = NULL;
	}

}

employee.cpp

#include "employee.h"
#include<string>

Employee::Employee(int id, string name, int dId) {
	this->m_Id = id;
	this->m_Name = name;
	this->m_DeId = dId;
}

void Employee::showInfo() {
	cout << "职工编号: " << this->m_Id
		<< "\t职工姓名: " << this->m_Name
		<< "\t岗位:  "<<this->getDeName()
		<< "\t岗位职责: 完成经理交给的任务" << endl;
}

string Employee::getDeName() {
	return "员工";
}

Manager.cpp

#include "manager.h"

Manager::Manager(int id, string name, int dId) {
	this->m_Id = id;
	this->m_Name = name;
	this->m_DeId = dId;
}

void Manager::showInfo() {
	cout << "职工编号: " << this->m_Id
		<< "\t职工姓名: " << this->m_Name
		<< "\t岗位:  " << this->getDeName()
		<< "\t岗位职责: 完成boss交给的任务,hand out undertake to employee" << endl;

}

string Manager::getDeName() {
	return "Manager";
}

Boss.cpp

#include "boss.h"

Boss::Boss(int id, string name, int dId) {
	this->m_Id = id;
	this->m_Name = name;
	this->m_DeId = dId;
}

void Boss::showInfo() {
	cout << "职工编号: " << this->m_Id
		<< "\t职工姓名: " << this->m_Name
		<< "\t岗位:  " << this->getDeName()
		<< "\t岗位职责: Control interprize everything" << endl;

}

string Boss::getDeName() {
	return "Boss";
}

EMSmain.cpp

#include<iostream>
#include<string>

#include "EMS.h"

using namespace std;

#include "employee.h"
#include"manager.h"
#include"boss.h"

void test() {

	//使用父类指针指向子类对象,体现多态性
	Worker* worker = NULL;
	worker = new Employee(1, "zhangsan", 1);
	worker->showInfo();
	delete worker;

	worker = new Manager(1, "LiSi", 1);
	worker->showInfo();
	delete worker;

	worker = new Boss(1, "WangWu", 1);
	worker->showInfo();
	delete worker;


	/*Employee e1(1, "张十三", 2);
	e1.showInfo();*/
}

int main() {

	/*test();*/

	EmployerManager em;
	int choice = 0;
	while (true) {
		em.showMenu();
		cout << "请输入你的选择操作" << endl;

		cin >> choice;
		switch (choice) {
		case 0:
			em.exitSystem();
			break;
		case 1:
			em.add_worker();
			break;
		case 2:
			em.show_worker();
			break;
		case 3:
			em.delete_worker();
			break;
		case 4:
			em.modify_worker();
			break;
		case 5:
			em.find_worker();
			break;
		case 6:
			em.sort_worker();
			break;
		case 7:
			em.clean_file();
			break;
		default:
			system("cls");
			break;
		}
	}

	

	system("pause");
	return 0;
}

相关代码上传至github:https://github.com/kair998/EmployerManagerSystem

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值