文章目录
第七章运算符重载
一、运算符重载定义
-
函数类型 operator 运算符名称 (形参表){对运算符进行处理}
-
比如重载Complex类型的+:Complex operator +(Complex &c1,Complex &c2)
-
c3 = c1+c2就相当于c3 = operator+(c1,c2)
-
不能重载的运算符
- .——成员运算符
- *——成员指针运算符
- ::——域运算符
- sizeof——长度运算符
- ?:——条件运算符
-
将运算符重载为类的成员函数时,可以少写一个参数,但是左侧必须是与运算符函数类型相同
-
当运算符函数重载为友元函数时,参数不能省略,参数的顺序必须与定义的类型相同
-
只能作为类的成员函数重载的运算符:=,[],(),->
-
只能作为友元函数来进行重载的运算符:<<,>>
写项目的思想:先搭框架,逐步扩充,由简到繁,最后完善。边编程,边调试
二、重载单目运算符
1.重载自增++运算符
#include <iostream>
using namespace std;
class Time
{public:
Time(){minute=0;sec=0;}
Time(int m,int s):minute(m),sec(s){}
Time operator++();
Time operator++(int);
void display(){cout<<minute<<":"<<sec<<endl;}
private:
int minute;
int sec;
};
Time Time::operator++()
{if(++sec>=60)
{sec-=60;
++minute;}
return *this;
}
Time Time::operator++(int)
{Time temp(*this);
sec++;
if(sec>=60)
{sec-=60;
++minute;}
return temp;
}
int main()
{Time time1(34,59),time2;
cout<<" time1 : ";
time1.display();
++time1;
cout<<"++time1: ";
time1.display();
time2=time1++;
cout<<"time1++: ";
time1.display();
cout<<" time2 : ";
time2.display();
return 0;
}
三、重载流运算符
- istream & operator >>(istream &,自定义类);
- ostream & operator <<(ostream &,自定义类);
1.重载流插入运算符
#include <iostream.h>
class Complex
{public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
Complex operator + (Complex &c2);
friend ostream& operator << (ostream&,Complex&);
private:
double real;
double imag;
};
Complex Complex::operator + (Complex &c2)
{return Complex(real+c2.real,imag+c2.imag);}
ostream& operator << (ostream& output,Complex& c)
{output<<"("<<c.real<<"+"<<c.imag<<"i)"<<endl;
return output;
}
int main()
{Complex c1(2,4),c2(6,10),c3;
c3=c1+c2;
cout<<c3;
return 0;
}
- output是cout的引用——即别名——cout<<c3解释为 operator<<(cout,c3)
同理,重载流提取也是如此。
重载运算符只能把一个运算符用于一个指定的类
四、不同数据类型之间的转换
1.转换构造函数
- 隐式转换与显式转换——类型名(数据)
- 转换构造函数——若c = c1+2.5因为c1是Complex的类型,重载了+,所以不能直接相加,先Complex(2.5),再相加
- 转换构造函数只能有一个参数
2.类型转换函数
- 将一个类的对象转换成另一个数据类型——且只能作为成员函数,转换的主体是类的对象
- operator (){实现转换的语句}——在函数名面前不能指定类型,函数没有参数
#include <iostream>
using namespace std;
class Complex
{public:
Complex(){real=0;imag=0;}
Complex(double r){real=r;imag=0;}
Complex(double r,double i){real=r;imag=i;}
friend Complex operator + (Complex c1,Complex c2);
void display();
private:
double real;
double imag;
};
Complex operator + (Complex c1,Complex c2)
{return Complex(c1.real+c2.real, c1.imag+c2.imag);}
void Complex::display()
{cout<<"("<<real<<"+"<<imag<<"i)"<<endl;}
int main()
{Complex c1(3,4),c2(5,-10),c3;
c3=c1+2.5;
c3.display();
return 0;
}
第八章继承与派生
一、继承与派生的概念
1.相关知识
-
一个子类有多个父类,一个父类可以有多个子类
-
派生类的一般形式:class 派生类名 :[继承方式] 基类名{派生类新增加的成员}
-
私有基类的私有成员在派生类中不可访问,只能通过成员函数可以引用他们。
2.继承的方式以及继承后的属性总结
编译方式 | public | private | protected |
---|---|---|---|
public | public | X | protected |
private | private | X | private |
protected | protected | X | protected |
二、派生类的构造函数和析构函数
1.简单的派生类的构造函数
- 派生类构造函数名(总参数表):基类构造函数名(参数表){派生类中新增数据成员初始化语句}
Student(int n,string nam,int a,string ad):Student(n,nam){
age = a;
addr=ad;
}
2.含子对象的派生类的构造函数
-
含子对象的意思是:在基类存在一个对象需要在派生类中初始化
-
派生类构造函数名(总参数表):基类构造函数名(参数表),子对象名(参数表){派生类中新增数据成员初始化语句}
下面是含基类子对象的派生类的构造函数
Student(int n,string nam,int nl,string naml,int a,string ad):Student(n,nam),monitor(nl,naml){
age = a;
addr=ad;
}
- 执行顺序
- 先调用基类构造函数
- 在调用子对象的构造函数
- 在调用派生类构造函数
3.多层派生时的构造函数
#include <iostream>
#include<string>
using namespace std;
class Student //声明基类
{public: //公用部分
Student(int n, string nam ) //基类构造函数
{num=n;
name=nam;
}
void display() //输出基类数据成员
{cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
}
protected: //保护部分
int num; //基类有两个数据成员
string name;
};
class Student1: public Student //声明公用派生类Student1
{public:
Student1(int n,char nam[10],int a):Student(n,nam) //派生类构造函数
{age=a; } //在此处只对派生类新增的数据成员初始化
void show( ) //输出num,name和age
{display(); //输出num和name
cout<<"age: "<<age<<endl;
}
private: //派生类的私有数据
int age; //增加一个数据成员
};
class Student2:public Student1 //声明间接公用派生类student2
{public:
//下面是间接派生类构造函数
Student2(int n, string nam,int a,int s):Student1(n,nam,a)
{score=s;}
void show_all() //输出全部数据成员
{show(); //输出num和name
cout<<"score:"<<score<<endl; //输出age
}
private:
int score; //增加一个数据成员
};
int main( )
{Student2 stud(10010,"Li",17,89);
stud.show_all( ); //输出学生的全部数据
return 0;
}
- 不要列出每一层派生类的构造函数,只需要写出在其上一层派生类的构造函数即可
- 在基类中没有定义构造函数或者定义了没有参数的构造函数时,在派生类中可以不写基类构造函数
- 派生类不能继承基类的析构函数,在执行派生类的析构函数时,系统自动调用基类里的析构函数来进行清理
三、多重继承
1.多重继承的定义
class D:public A,private B,protected C{}
-
多重继承派生类的构造函数
- 派生类构造函数名(总的参数表):基类1构造函数(参数表),基类2构造函数(参数表),基类3构造函数(参数表){派生类新增数据成员初始化语句}
-
调用基类构造函数的顺序是按照声明派生类时基类的出现顺序来定
#include <iostream>
#include <string>
using namespace std;
class Teacher //声明Teacher(教师)类
{public: //公用部分
Teacher(string nam,int a,string t) //构造函数
{name=nam;
age=a;
title=t;}
void display() //输出教师有关数据
{cout<<"name:"<<name<<endl;
cout<<"age"<<age<<endl;
cout<<"title:"<<title<<endl;
}
protected: //保护部分
string name;
int age;
string title; //职称
};
class Student //声明类Student(学生)
{public:
Student(string nam,char s,float sco)
{name1=nam;
sex=s;
score=sco;} //构造函数
void display1() //输出学生有关数据
{cout<<"name:"<<name1<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"score:"<<score<<endl;
}
protected: //保护部分
string name1;
char sex;
float score; //成绩
};
class Graduate:public Teacher,public Student //声明多重继承的派生类Graduate
{public:
Graduate(string nam,int a,char s,string t,float sco,float w):
Teacher(nam,a,t),Student(nam,s,sco),wage(w) {}
void show( ) //输出人员的有关数据
{cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"score:"<<score<<endl;
cout<<"title:"<<title<<endl;
cout<<"wages:"<<wage<<endl;
}
private:
float wage; //工资
};
int main( )
{Graduate grad1("Wang-li",24,'f',"assistant",89.5,1234.5);
grad1.show( );
return 0;
}
- 但该程序存在问题——两个基类中的姓名一样,但是有两个变量name和name1,数据出现重复
2.多重继承的二义性问题
若C类继承A类和B类
C类:int a;int A::a;int B::a;void display();void A::display();void B::display();
C c1;
c1.a=3;
c1.display();
- 该程序可以正常运行,基类的同名成员在派生类中被覆盖,c1.a相当于是C类里面的数据成员
- 但同时会面临着多份数据被拷贝占用空间的问题
3.虚基类
- 作用:在继承间接共同基类时只保留一份成员
- 虚基类是在派生类声明时,指定继承方式中声明的
- 形式:class 派生类名:virtual 继承方式 基类名
- 注意:以前在多继承中,对派生类构造函数只需对其直接基类初始化即可,但声明了虚基类后,直接和间接积累都需要初始化。
#include <iostream>
#include <string>
using namespace std;
//定义公共基类Person
class Person
{public:
Person(char *nam,char s,int a) //构造函数
{strcpy(name,nam);sex=s;age=a;}
protected: //保护成员
char name[20];
char sex;
int age;
};
//定义类Teacher
class Teacher:virtual public Person //声明Person为公用继承的虚基类
{public:
Teacher(char *nam,char s,int a,char *t):Person(nam,s,a) //构造函数
{strcpy(title,t);
}
protected: //保护成员
char title[10]; //职称
};
//定义类Student
class Student:virtual public Person //声明Person为公用继承的虚基类
{public:
Student(char *nam,char s,int a,float sco): //构造函数
Person(nam,s,a),score(sco){} //初始化表
protected: //保护成员
float score; //成绩
};
//定义多重继承的派生类Graduate
class Graduate:public Teacher,public Student //声明Teacher和Student类为公用继承的直接基类
{public:
Graduate(char *nam,char s,int a,char *t,float sco,float w): //构造函数
Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){} //初始化表
void show( ) //输出研究生的有关数据
{cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"score:"<<score<<endl;
cout<<"title:"<<title<<endl;
cout<<"wages:"<<wage<<endl;
}
private:
float wage; //工资
};
int main( )
{Graduate grad1("Wang-li",'f',24,"assistant",89.5,1234.5);
grad1.show( );
return 0;
}
重点观察在派生类Graduate中的参数初始化表
4.基类与派生类之间的转化
- 只能用子类对象对基类对象赋值,而不能用基类对象对其子类对象赋值
- 通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类中增加的成员
#include <iostream>
#include <string>
using namespace std;
class Student
{public:
Student(int,string,float);
void display();
private:
int num;
string name;
float score;
};
Student::Student(int n,string nam,float s)
{num=n;
name=nam;
score=s;
}
void Student::display()
{cout<<endl<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"score:"<<score<<endl;
}
class Graduate:public Student
{public:
Graduate(int,string,float,float);
void display();
private:
float pay;
};
void Graduate::display()
{Student::display();
cout<<"pay="<<pay<<endl;
}
Graduate::Graduate(int n,string nam,float s,float p):Student(n,nam,s),pay(p){}
int main()
{Student stud1(1001,"Li",87.5);
Graduate grad1(2001,"Wang",98.5,563.5);
Student *pt=&stud1;
pt->display();
pt=&grad1;
pt->display();
return 0;
}
5.组合
- 在一个类中以另一个类中的对象作为数据成员,叫做类的组合。继承是纵向的,组合是横向的。
void fun1(Teacher &);
void fun2(Birthday &);
Teacher pro;Birthday birth;
fun1(pro);//正确
fun2(pro.birth)//正确
fun2(pro);//错误