回顾:
Review cpp day01
Review cpp day02
Review cpp day03
Review cpp day04
Review cpp day05
Review cpp day06
十九、成员指针//了解
1、成员变量指针
- 1)定义:
类型 类名::*指针变量名 = &类名::变量;
- 2)使用
- 对象.*成员指针变量名;
注: ".*"称为成员指针解引用操作符 - 对象指针->*成员指针变量名;
注: "->*"称为间接成员指针解引用操作符
- 对象.*成员指针变量名;
01menptr.cpp
#include <iostream>
using namespace std;
class Student{
public:
Student(const string& name):m_name(name){}
string m_name;
};
int main(void){
string Student::*pname = &Student::m_name;
Student s("张飞");
Student* ps = new Student("赵云");
cout << s.*pname << endl;//张飞
cout << ps->*pname << endl;//赵云
delete ps;
return 0;
}
2、成员函数指针
- 1)定义:
返回类型 (类名::*成员函数指针)(形参表) = &类名::成员函数名;
- 2)使用
- (对象.*成员函数指针)(实参表);
- (对象指针->*成员函数指针)(实参表);
02menptr.cpp
#include <iostream>
using namespace std;
class Student{
public:
Student(const string& name):m_name(name){}
void who(void){
cout << m_name << endl;
}
string m_name;
};
int main(void){
void (Student::*pwho)(void) = &Student::who;
Student s("张飞");
Student* ps = new Student("赵云");
(s.*pwho)();//张飞
(ps->*pwho)();//赵云
delete ps;
return 0;
}
二十、操作符重载
eg:复数 x+yi, 实现(1+2i) + (3+4i) = 4 + 6i
1、双目操作符的重载 L#R
1.1、运算类的双目操作符:+ - …
- 左右操作数既可以是左值也可以是右值
- 表达式结构是右值
- 两种实现方式:
- 1)成员函数形式
L#R
的表达式可以被编译器翻译成L.operator#(R)
这样成员函数调用形式,该函数的返回结果即使表达式的结果。 - 2)全局函数形式
L#R
的表达式可以被编译器翻译成::operator#(L, R)
这样全局函数调用形式,该函数的返回结果即使表达式的结果。
注: 使用“friend”关键字,可以把一个全局函数声明为某个类的友元,友元函数可以访问类中任何成员。
- 1)成员函数形式
03complex.cpp(第一种实现方式)
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r, int i):m_r(r), m_i(i){}
void print(void)const{
cout << m_r << '+' << m_i << 'i' << endl;
}
//c1+c2==>c1.operator+(c2)
/*
*const关键字的意义:
*1)修饰返回值,为了返回右值,即返回的对象只能是右值
*2)常引用,为了支持常量型右操作数(右值),即传入的形参c即可以是普通引用,也可以是常引用
*3)常函数,为了支持常量型左操作数(左值),即调用operator+的对象是常引用对象,也可以是普通对象
*/
const Complex operator+(const Complex& c)const{
Complex res(m_r+c.m_r, m_i+c.m_i);
return res;
}
private:
int m_r;//实部
int m_i;//虚部
};
int main(void){
Complex c1(1, 2);
Complex c2(3, 4);
c1.print();
c2.print();
c1.operator+(c2);
Complex c3 = c1 + c2;
c3.print();//4+6i
return 0;
}
03complex.cpp(第二种实现方式)
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r, int i):m_r(r), m_i(i){}
void print(void)const{
cout << m_r << '+' << m_i << 'i' << endl;
}
//c1+c2==>c1.operator+(c2)
/*
*const关键字的意义:
*1)修饰返回值,为了返回右值
*2)常引用,为了支持常量型右操作数(右值)
*3)常函数,为了支持常量型左操作数(左值)
*/
const Complex operator+(const Complex& c)const{
Complex res(m_r+c.m_r, m_i+c.m_i);
return res;
}
private:
int m_r;//实部
int m_i;//虚部
//友元函数可以访问类中的私有成员
friend const Complex operator-(const Complex& l, const Complex& r);
};
const Complex operator-(const Complex& l, const Complex& r){
Complex res(l.m_r-r.m_r, l.m_i-r.m_i);
return res;
}
int main(void){
Complex c1(1, 2);
Complex c2(3, 4);
c1.print();
c2.print();
c1.operator+(c2);
Complex c3 = c1 + c2;
c3.print();//4+6i
//::operator-(c2, c1)
c3 = c2 - ci;
c3.print();//2+2i
return 0;
}
1.2、赋值类的双目操作符:+= -= …
- 左操作数必须是左值,右操作数可以是左值也可以是右值
- 表达式的结果是左值,就是左操作数的自身。
int a = 3, b= 5;
(a += b) = 10;//a+=b,a=10
(a += 5) = 10;
- 两种实现方式:
- 1)成员函数形式
L # R ==》L.operator#(R)
- 2)全局函数形式
L # R ==》::operator#(L, R)
- 1)成员函数形式
04complex.cpp
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r, int i):m_r(r), m_i(i){}
void print(void)const{
cout << m_r << '+' << m_i << 'i' << endl;
}
//+=:成员函数形式
Comple& operator+=(const Complex& c){
m_r += c.m_r;
m_i += c.m_i;
return *this;
}
//友元函数可以把定义直接写在类的内部,但是它不属于类,本质上还是全局函数
friend Complex& operator-=(Complex& l, const Complex& r){
l.m_r -= r.m_r;
l.m_i -= r.m_i;
return l;
}
private:
int m_r;//实部
int m_i;//虚部
};
int main(void){
Complex c1(1, 2);
Complex c2(3, 4);
c1 += c2;//c1.operator+=(c2)
c1.print();//4+6i
c1 -= c2;//::operator-=(c1, c2)
c1.print();//1+2i
return 0;
}
2、单目操作符重载#O
2.1、计算类的单目操作符:-(取负) ~…
- 操作数可以是左值也可以是右值
- 表达式结果是右值
- 两种实现方式:
- 1)成员函数形式:
#O ==》O.operator#();
- 2)全局函数形式
#O ==》::operator#(O);
- 1)成员函数形式:
05Integer.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int i=0):m_i(i){}
void print(void)const{
cout << m_i << endl;
}
//-:成员函数形式
const Integer operator-(void)const{
Integer res(-m_i);
return res;
}
//~:全局函数形式(自定义表示乘方)
friend const Integer operator~(const Integer& i){
Integer res(i.m_i * i.m_i);
return res;
}
private:
int m_i;
};
int main(void){
Integer i(100);
Integer j = -i;//i.operator-();
j.print();//100
j = ~i;
j.print();//10000
return 0;
}
2.2、自增减单目操作符:前后++、--
- 1)前
++、--
- 操作数必须是左值;
- 表达式结果也是左值,就是操作数的自身。
- 两种实现方式:
- 成员函数 #O==》O.operator#()
- 全局函数 #O==》::operator#(O)
int a = 1;
cout << ++a << endl;//2
cout << a << endl;//2
++a = 10;//ok
cout << a << endl;//10
++++++a;//ok
cout << a << endl;//13
- 2)后
++、--
- 操作数必须是左值;
- 表达式结果是右值,是操作数自增减前的副本。
- 两种实现方式:
- 成员函数 O#==》O.operator#(哑元)
- 全局函数 O#==》::operator#(O,哑元)
注: 哑元只是为了区分前、后++, --
int a = 1;
cout << a++ << endl;//1
cout << a << endl;//2
06Integer.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int i=0):m_i(i){}
void print(void)const{
cout << m_i << endl;
}
//前++:成员函数形式
Integer& operator++(void){
++m_i;
return *this;
}
//前--:全局函数形式
friend Integer& operator--(Integer& i){
--i.m_i;
return i;
}
//后++:成员函数形式
const Integer operator++(int/*哑元*/){
Integer old = *this;
++*this;//++m_i;
return old;
}
//后--:全局函数形式
friend const Integer operator--(Integer& i, int/*哑元*/){
Integer old = i;
i--;
return old;
}
private:
int m_i;
};
int main(void){
Integer i(100);
Integer j = ++i;//i.operator++()
i.print();//
j.print();//
j = ++++++i;
i.print();//104
j.print();//104
j = --i;//::operator--(i)
i.print();//103
j.print();//103
j = ------i;
i.print();//100
j.print();//100
j = i++;//i.operator++(0)
i.print();//101
j.print();//100
j = i--;//::operator--(i, 0)
i.print();//100
j.print();//101
return 0;
}
3、插入和提取操作符:<< >>
- 功能:实现自定义类型的对象的直接输入和输出
注: 只能用全局函数形式,因为ostream cout 与 istream cin
是c++的标准库中的东西,难以改变源码,所以只能用全局函数形式::operator<<(cout/cin, a);
07io_operator.cpp
#include <iostream>
ostream cout;
istream cin;
//通用形式,只要将RIGHT& 替换为对应的类名就行
friend ostream& operator<<(ostream& os, const RIGHT& right){......}
friend istream& operator>>(istream& is, RIGHT& right){......}
cout << a << b << c;//cout<< a cout << b << c; cout << a cout << b cout << c;
cin >> a >> b >> c;
03complex.cpp
#include <iostream>
using namespace std;
class Complex{
public:
Complex(int r, int i):m_r(r), m_i(i){}
friend ostream& operator<<(ostream& os, const Complex& c){
os << c.m_r << "+" << c.m_i << "i";
return os;
}
friend istream& operator>>(istream& is, Complex& c){
is >> c.m_r >> c.m_i;
return is;
}
private:
int m_r;//实部
int m_i;//虚部
};
int main(void){
Complex c1(1, 2);
Complex c2(3, 4);
//::operator << (cout, c1);
cout << c1 << endl;
cout << c1 << ',' << c2 << endl;
Complex c3(0, 0);
cout << "请输入一个复数的(实部和虚部):";
cin >> c3;//::operator>>(cin, c3);
cout << c3 << endl;
return 0;
}
- 练习:实现一个3*3矩阵,支持如下操作符重载:+ - += -(负) <<(输出)
class M33{
public:
M33(void){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++0){
m_a[i][j] = 0;
}
}
}
M33(int a[][3]){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++0){
m_a[i][j] = a[i][j];
}
}
}
private:
int m_a[3][3];
};
int main(void){
int a1[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
M33 m1(a1);
int a2[3][3] = {9, 8, 7, 6, 5, 4, 3, 2, 1};
M33 m2(a2);
return 0;
}
08M33.cpp
#include <iostream>
#include <iomanip>//setw(4)引用的头文件
using namespace std;
class M33{
public:
M33(void){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++0){
m_a[i][j] = 0;
}
}
}
M33(int a[][3]){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++0){
m_a[i][j] = a[i][j];
}
}
}
friend ostream& operator<<(ostream& os, const M33& m){
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
//setw(4):设置显示的域宽
os << setw(4) << m.m_a[i][j];
}
os << endl;
}
return os;
}
const M33 operator+(const M33& that)const{
int res[3][3] = {0};
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
res[i][j] = m_a[i][j] + that.m_a[i][j];
}
}
return M33 (res);//临时类对象的可以省略类名
}
const M33 operator-(const M33& that)const{
int res[3][3] = {0};
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
res[i][j] = m_a[i][j] - that.m_a[i][j];
}
}
return M33 (res);//临时类对象的可以省略类名
}
M33& operator+=(const M33& m){
*this = *this + m;//函数复用,使用上面的operator+
return *this
}
M33& operator-=(const M33& m){
*this = *this - m;//函数复用,使用上面的operator-
return *this
}
const M33 operator-(void)const{
//M33(): 以无参形式创建临时对象
return M33() - *this;
}
private:
int m_a[3][3];
};
int main(void){
int a1[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
M33 m1(a1);
int a2[3][3] = {9, 8, 7, 6, 5, 4, 3, 2, 1};
M33 m2(a2);
cout << m1 << endl;
cout << m2 << endl;
cout << "m1+m2:" << endl;
cout << m1+m2 << endl;
cout << "m1-m2:" << endl;
cout << m1-m2 << endl;
cout << "m1+=m2:" << endl;
cout << m1+=m2 << endl;
cout << m1 << endl;
cout << "m1-=m2:" << endl;
cout << m1-=m2 << endl;
cout << m1 << endl;
return 0;
}
4、下标操作符重载:[]
- 功能:让一个对象像数组一样去使用
注: 非-常对象返回左值,常对象返回右值
string s = "hello";
//s.operator[](0)
s[0] = 'H'//ok
const string s2 = "hello";
//s2.operator[](0)
s2[0] = 'H';//error
09array_operator.cpp
#include <iostream>
using namespace std;
//定义表示容器的类,里面存放多个int
class Array{
public:
Array(size_t size){
m_data = new int[size];
}
~Array(void){
delete[] m_data;
}
int& operator[](size_t i){
return m_data[i];
}
const int& operator[](size_t i)const{
return m_data[i];
}
private:
int *m_data;
};
int main(void){
Array arr(10);
arr[0] = 100;
arr[1] = 200;
cout << arr[0] << ',' << arr[1] << endl;
const Array& rarr = arr;
cout << rarr[0] << ',' << rarr[1] << end;
//rarr[1] = 123;//error
return 0;
}
5、函数操作符:()
- 功能:让一个对象可以像函数一样使用
注: 对参数个数、参数类型、返回值类型没有限制。
class A{...};
A a;
a(100, 200);//a.operator()(100,200)
10func_operator.cpp
#include <iostream>
using namespace std;
class Func{
public:
double operator()(double d){
return d*d;
}
};
int main(void){
Func func;
//func.operator()(3.14)
cout << func(3.14) << endl;
return 0;
}
6、new/delete操作符
static void* operator new(size_t size){...}
static void operator delete(void* p){...}
11new_operator.cpp
#include <iostream>
#include <cstdlib>
using namespace std;
class A{
public:
A(void){
cout << "A::A()" << endl;
}
~A(void){
cout << "A::~A()" << endl;
}
static void* operator new(size_t size){
cout << "A::new" << endl;
void* pv = malloc(size);
return pv;
}
static void operator delete(void* pv){
cout << "A::delete" << endl;
free(pv);
}
};
int main(void){
//1) A* pa = (A*)A::operator new(sizeof(A))
//2) pa->A::A()
A* pa = new A;
//1) pa->A::~A()
//A::operator delete(pa);
delete pa;
return 0;
}
总结:操作符重载的限制
-
1)不是所有的操作符都可以重载,下列操作符不能重载
- 作用域限定符“::”
- 直接成员访问操作符“.”
- 直接成员指针解引用操作符“.*”
- 条件操作符“?:”
- 字节长度操作符“sizeof”
- 类型信息操作符“typeid”(后面讲)
-
2)如果一个操作符的所有操作数都是基本类型,则无法实现重载
eg:int operator+(int a, int b){return a-b;}//error
-
3)操作符重载不会改变编译器预定义的优先级
-
4)操作符的重载不能改变操作数的个数
-
5)无法通过操作符重载发明新的操作符
eg:operator@(...){...}//error
扩展练习V5.0:实现企业员工管理系统
- 需求:为员工类增加:<< == != 操作符支持
class Employee{
ostream& operator<<(ostream& os, const Employee& emp){...}
//根据员工的ID号和参数id比较是否相等
bool operator==(int id){}
bool operator!=(int id){}
};
Employee.h
#include <iostream>
#include <cstdio>
using namespace std;
class Employee{
public:
Employee(const string& name, double salary);
~Employee(void);
void printInfo(void)const;
void claSalary(void);
void setId(int id);
void setName(const string& name);
void setSalary(double salary);
void saveInfo(void)const;
//根据员工对象的id和参数id比较,是否相等
bool operator==(int id)const;
bool operator!=(int id)const;
friend ostream& operator<<(ostream& os, const Employee& emp);
private:
//禁止拷贝构造函数和拷贝复制
Employee& operator = (const Employee&);
Employee(const Employee&);
private:
int m_id;//工号
string m_name;//姓名
double m_salary;//工资
FILE* file;
public:
//获取员工人数
static const int& getEmployeeCount(void);
private:
static int m_count;//记录员工人数
};
Employee.cpp
#include "Employee.h"
Employee::Employee(const string& name, double salary)
:m_name(name), m_saraly(salary){
//读取ID
FILE* fp = fopen("id.txt", "r+");
fscanf(fp, "%d", &m_id);
//将文件读写指针定位到文件头
fseek(fp, 0, SEEL_SET);
//ID+1, 再保存回id.txt
fprintf(fp, "%d", m_id+1);
//关闭id.txt
fclose(fp);
//将ID转换为字符串
char filename[20] = {0};
sprintf(filename, "%d", m_d);
//根据id创建文件保存当前员工信息
file = fopen(filename, "w");
//保存员工信息
saveInfo();
//记录员工人数
++m_count;
//保存与纳贡人数
FILE* fp2 = fopen("count.txt", "w");
fprintf(fp2, "%d", m_count);
fclose(fp2);
}
Employee::~Employee(void){
fclose(file);
file =NULL;
}
Employee::Employee(const Employee& that){
char filename[20] = {0};
sprintf(filename, "%d", that.m_id);
file = fopen(filename, "r+");
m_id = that.m_id;
m_name = that.m_name;
m_salary = that.m_salary;
}
void Employee::printInfo(void)const{
cout << "姓名:" << m_name << endl;
cout << "工号:" << m_id << endl;
cout << "基础工资:" << m_salary << endl;
}
void Eployee::calSalary(void){
cout << "请输入出勤天数:";
int days;
cin >> days;
double basic = m_salary * (days/23.0);
double merit = basic / 2;
cout << "总工资为:" << (basic + merit) << endl;
}
void Employee::setid(int){
if(id < 10000){
cout << "无效的工号" << endl;
}
else{
m_id = id;
}
}
void Employee::setName(const string& name){
if(name.size() > 20){
cout << "无效的姓名" << endl;
}
else{
m_name = name;
}
}
void Employee::setSalary(double){
if(salary < 0){
cout << "无效工资" << endl;
}
else{
m_salary = salary;
}
}
void Employee::saveInfo(void)const{
fseek(file, 0, SEEK_SET);
fprintf(file, "%d %s %g", m_id, m_name.c_str(), m_salary);
}
//静态成员需要在类的外部单独定义和初始化
int Employee::m_count = 0;
count int& Employee::getEmployeeCount(void){
7this FILE* fp = fopen("count.txt", "r");
//加载员工人数
fscanf(fp, "%d", &m_count);
fclose(fp);
return m_count;
}
//根据员工对象的id和参数id比较,是否相等
bool Employee::operator==(int id)const{
if(m_id ==id){
return true;
}
else{
return false;
}
}
bool Employee::operator!=(int id)const{
return !(*this == id);//函数复用
}
//全局函数
ostream& operator<<(ostream& os, const Employee& emp){
os << "Employee(" << emp.m_id << "," << emp.m_name << em.m_salary << ")";
return os;
}
main.cpp
#include "Employee.h"
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
void init(void){
if(access("id.txt", F_OK) == -1){
FILE* fp = fopen("id.txt", "w");//记录工号
if(fp == NULL){
perror("fopen"),exit(-1);
}
fprintf(fp, "%d", 10001);
fclose(fp);
}
if(access("count.txt", F_OK) == -1){
FILE* fp = fopen("count.txt", "w");//记录人数
if(fp == NULL){
perror("fopen"),exit(-1);
}
fprintf(fp, "%d", 0);
fclose(fp);
}
else{
//加载员工人数
Employee::getEmployeeCount();
}
}
int main(void){
init();
Employee emp("张三", 6600);
//emp.printInfo();
cout << emp << endl;
emp.calSalary();
if(emp == 10001){
cout << "true" << endl;
}
else{
cout << "false" << endl;
}
return 0;
}