C++程序设计(谭浩强)笔记六

第七章运算符重载

一、运算符重载定义

  • 函数类型 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.继承的方式以及继承后的属性总结
编译方式publicprivateprotected
publicpublicXprotected
privateprivateXprivate
protectedprotectedXprotected

二、派生类的构造函数和析构函数

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);//错误
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Indra_ran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值