函数重载:
在C++中允许相同函数名的函数存在,而在C语言中不允许同名的函数存在
原因:在C语言中编译以后函数名对应的标号为: _函数名,也就是说在C语言中,比如有如下函数:
void test_fun(int a, int b)
编译以后函数名会变成标号: _test_fun
而C++中函数编译以后的标号为: _函数名_参数类型列表
上面的函数编译后的名称为: _test_fun_int_int
比如还有一个函数: void test_fun(int a, double b)编译以后对应的标号:_test_fun_int_double
所以C++中上面两个函数可以同时存在
重载: 对于相同名字的函数存在不同的版本,在函数调用的时候可以根据传递的实参的不同来选择不同版本的函数去执行
/*
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2023-08-01 09:20:10
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2023-08-01 09:31:48
* @FilePath: \vscode\cpp_source\CPP_202308\CPP_02 面向对象基础\函数重载测试.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include <iostream>
using namespace std;
int my_max(int a, int b)
{
cout << "两个整数参数: " << endl;
return a > b ? a : b;
}
double my_max(double a, double b)
{
cout << "两个实数参数: " << endl;
return a > b ? a : b;
}
int my_max(int a, int b, int c)
{
cout << "三个个整数参数: " << endl;
return my_max(a, b) > c ? my_max(a, b) : c;
}
int main()
{
int n1 = 12, n2 = 8, n3 = 9;
double d1 = 1.35, d2 = 3.57, d3 = 9.71;
//在进行函数调用的时候,会自动根据参数的不同来调用不同版本的函数
cout << my_max(n1, n2) << endl;
cout << my_max(d1, d2) << endl;
cout << my_max(n1, n2, n3) << endl;
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> g++ .\函数重载测试.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> ./test
两个整数参数:
12
两个实数参数:
3.57
三个个整数参数:
两个整数参数:
两个整数参数:
12
当只有一个版本的函数的时候,如果传递的参数的类型和形参的类型兼容,这时候会将实参隐式转换为形参的类型然后去调用该函数
#include <iostream>
using namespace std;
/*
int my_max(int a, int b)
{
cout << "两个整数参数: " << endl;
return a > b ? a : b;
}
*/
double my_max(double a, double b)
{
cout << "两个实数参数: " << endl;
return a > b ? a : b;
}
/*
int my_max(int a, int b, int c)
{
cout << "三个个整数参数: " << endl;
return my_max(a, b) > c ? my_max(a, b) : c;
}
*/
int main()
{
int n1 = 12, n2 = 8, n3 = 9;
double d1 = 1.35, d2 = 3.57, d3 = 9.71;
//在进行函数调用的时候,会自动根据参数的不同来调用不同版本的函数
cout << my_max(n1, n2) << endl;
cout << my_max(d1, d2) << endl;
//cout << my_max(n1, n2, n3) << endl;
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> g++ .\函数重载测试.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> ./test
两个实数参数:
12
两个实数参数:
3.57
注意: 只有函数的返回类型不同,不构成重载(不允许两个函数只有返回类型不同);函数的形参名不同也不构成重载;inline和static都不能构成重载
重载: 同名不同参
面向对象:
面向结构(结构化编程): 问题分解
面向对象(OOP程序设计): 责任分解(自己的事情自己做)
面向对象的基本特征: 抽象(abstract),封装(encapsulation),继承(inheritance),多态(polymorphism)。
对象:现实世界中的一切
类:从现实世界中的同类对象中得到共同具有的属性和行为,把这些属性和行为封装到一个整体就形成类
抽象: 从事物的与当前目标有关的属性中找出事物的本质特征,把具有共同性质的事物划分为一类,得出一个抽象的概念
封装: 将从对象中抽象出来的属性和行为包装在一个类中。具体在封装的时候一般尽可能隐藏对象的属性,而向外提供一些操作属性的接口(行为) -- 把数据成员私有化,而向外提供一些公共的接口
继承: 通过已经存在的类来定义新类的一种方式,通过继承新的类具有已经存在的类的所有属性和方法。继承是C++中实现代码重用的一种重要机制
多态: 允许不同类的对象对同一消息作出不同的响应 -- 不同类型的对象对同一种行为有不同的操作 -- 在程序中的体现就是同一个函数会有不同的代码实现
类的定义:
类是一种用户自定义类型,定义格式:
class <类名>
{
private://私有成员
<私有数据成员和成员函数>;
protected://保护成员
<保护数据成员和成员函数>;
public: //公有成员
<公有数据成员和成员函数>;
};
<各个成员函数的实现>;
在类的定义中分为数据成员(属性)和成员函数(行为);可以通过private、protected、public来定义或者指定成员的访问属性(权限); 在类的定义中成员函数可以定义在类中(如果在类中实现,编译器会默认的将其当做内联函数来处理),也可以在类的外面实现
#include <iostream>
#include <cstring> //string.h
using namespace std;
//类的定义: 从同类对象得到共同具有的属性和行为的过程是抽象
class Student
{
//private: //一般在类的定义中,属性(数据成员)私有化 -- 私有成员在类的外部是不能访问的
public:
int s_no;
char s_name[12];
float s_sc;
public: //公有成员在类的外部是可以访问的
void show() //类的成员函数可以实现在类的内部
{
cout << "学号: " << s_no << ", 姓名: " << s_name << ", 成绩: " << s_sc << endl;
}
};
int main()
{
//类型必须实例化为具体的对象以后才能使用 -- 从类型定义对象的过程是实例化
Student st;
st.s_no = 1001;
strcpy(st.s_name, "Jack");
st.s_sc = 82.5f;
st.show(); //通过对象调用其成员要使用成员运算符
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> g++ .\类的定义实例.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> ./test
学号: 1001, 姓名: Jack, 成绩: 82.5
#include <iostream>
#include <cstring> //string.h
using namespace std;
//类的定义: 从同类对象得到共同具有的属性和行为的过程是抽象
class Student
{
private: //一般在类的定义中,属性(数据成员)私有化 -- 私有成员在类的外部是不能访问的
//public:
int s_no;
char s_name[12];
float s_sc;
public: //公有成员在类的外部是可以访问的
void show() //类的成员函数可以实现在类的内部
{
cout << "学号: " << s_no << ", 姓名: " << s_name << ", 成绩: " << s_sc << endl;
}
//向外提供一些公有的接口来访问私有的数据
void set_no(int no);
void set_name(const char *name);
void set_sc(float sc);
int get_no();
char *get_name();
float get_sc();
};
//成员函数可以在类的外部实现: 在类的外部实现成员函数的时候,在函数名前面需要使用类名加上域限定来执行该函数是属于哪个类的成员函数
void Student::set_no(int no)
{
s_no = no; //类的私有数据成员在类中是可以使用(可以在类的成员函数中直接访问类的私有数据成员)
}
void Student::set_name(const char *name)
{
if(NULL == name)
{
strcpy(s_name, "");
}
else
{
strcpy(s_name, name);
}
}
void Student::set_sc(float sc)
{
s_sc = sc;
}
int Student::get_no()
{
return s_no;
}
char *Student::get_name()
{
return s_name;
}
float Student::get_sc()
{
return s_sc;
}
int main()
{
//类型必须实例化为具体的对象以后才能使用 -- 从类型定义对象的过程是实例化
Student st;
/*
//在类的外部不能访问类中定义的私有数据成员
st.s_no = 1001;
strcpy(st.s_name, "Jack");
st.s_sc = 82.5f;
*/
st.set_no(1002);
st.set_sc(88.5f);
st.set_name("Smith");
st.show(); //通过对象调用其成员要使用成员运算符
//在类的外部通过类提供公有成员方法可以访问类的数据成员
cout << "姓名: " << st.get_name() << ", 成绩: " << st.get_sc() << endl;
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> g++ .\类的定义实例.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> ./test
学号: 1002, 姓名: Smith, 成绩: 88.5
姓名: Smith, 成绩: 88.5
类的构造函数: 类的构造函数是在类实例化对象的时候为对象分配空间并初始化对象的数据成员的时候自动调用的类的成员方法
#include <iostream>
#include <cstring> //string.h
using namespace std;
//类的定义: 从同类对象得到共同具有的属性和行为的过程是抽象
class Student
{
private: //一般在类的定义中,属性(数据成员)私有化 -- 私有成员在类的外部是不能访问的
int s_no;
char s_name[12];
float s_sc;
public: //公有成员在类的外部是可以访问的
void show() //类的成员函数可以实现在类的内部
{
cout << "学号: " << s_no << ", 姓名: " << s_name << ", 成绩: " << s_sc << endl;
}
Student()
{
cout << "类的无参构造函数" << endl;
s_no = 0;
s_sc = 0.0f;
strcpy(s_name, "");
}
Student(int no, const char *name, float sc)
{
cout << "类的带参构造函数" << endl;
s_no = no;
s_sc = sc;
strcpy(s_name, name);
}
};
int main()
{
Student st; //在实例化对象的时候会自动的调用构造函数来进行空间分配和成员的初始化;调用无参构造函数
st.show();
Student zhangsan(1003, "Smith", 88.5f); //调用带参数构造函数
zhangsan.show();
return 0;
}
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> g++ .\类的定义2.cpp -o test
PS E:\LinuxShared\vscode\cpp_source\CPP_202308\CPP_02 面向对象基础> ./test
类的无参构造函数
学号: 0, 姓名: , 成绩: 0
类的带参构造函数
学号: 1003, 姓名: Smith, 成绩: 88.5
构造函数的特点:
a. 构造函数名和类名相同,没有返回类型
b. 构造函数的作用是给对象分配内存空间(按照对象中数据成员依次连续存储)并初始化
c. 构造函数可以重载
d. 构造函数是在实例化对象的时候自动调用(会根据实例化的时候传递的参数的列表和对应的构造函数的版本进行比较选择合适版本的构造函数来实例化对象)
e. 类中默认的会自动生成一个无参构造函数;但当我们手动的在类中定义了某个版本的构造函数的时候,系统就不会生成无参构造函数了
在实例化对象的时候,数据成员的初始化可以在构造函数体内来进行,也可以在构造函数的初始值列表中进行
Student() : s_no(0), s_sc(0)
{
strcpy(s_name, "");
}
Student(int no, const char *name, float sc) : s_no(no), s_sc(sc)
{
cout << "类的带参构造函数" << endl;
strcpy(s_name, name);
}
注意:
初始值列表和在构造函数体内来初始化数据成员是有区别的: 在初始值表中的初始化是在分配数据成员的内存空间的同时初始化的(类似于int n = 12); 而函数体内的初始化是先给对象分配空间,然后再用构造函数中的初始化语句来初始化数据成员(类似于int n; n = 12;)
析构函数: 作用是在对象销毁的时候释放对象的空间的
析构函数的特点:
a. 函数名是~加上类名,析构函数没有返回类型,没有参数,不允许重载
b. 析构函数的作用是在对象销毁的时候释放对象的空间的
c. 析构函数是在对象销毁的时候自动调用的
d. 默认的类中会生成一个析构函数;一般情况下当类中有指针成员并且在构造函数中使用动态分配的方式来分配空间的时候,一定要手动实现析构函数在析构函数中释放动态分配的空间
注意: 在程序中按照对象定义的顺序调用构造函数进行初始化,按照构造的反顺序进行析构 -- 先构造的后析构,后构造的先析构
作业:
1. 定义一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,有两个公有成员函数run、stop。其中rank为枚举类型CPU_Rank,定义为enum CPU_Rank{P1=1,P2,P3,P4,P5,P6,P7},frequency为单位为MHz的整型数,voltage为浮点型的电压值。观察构造函数和析构函数的调用顺序。
2. 职员档案管理程序
设计一个简单的雇员档案管理程序。其中把雇员的档案数据和对这些数据的设置、修改、删除等操作组成一个程序模块。程序通过这个模块----类的公有部分对档案数据进行处理,实现了面向对象程序设计的“封装”功能。
3. 下面是一个类的测试程序,设计出能使用如下测试程序的类。
int main()
{
Test a;
a.init(68,55);
a.print();
}
执行结果为:
测试结果:68-55=13