您好,欢迎访问三七文档
当前位置:首页 > 财经/贸易 > 资产评估/会计 > 14北京大学程序设计实习课程
《程序设计实习》课程(C++ProgrammingPractice)程序设计实习第十六讲继承主讲教师:田永鸿yhtian@pku.edu.cn://idm.pku.edu.cn/jiaoxue-CPP/cpp08.htm2008年4月28日北京大学《程序设计实习》课程2课堂问题1.解释下面的操作符是否应定义为类成员,为什么?(a)+;(b)+=;(c)++;(d)-;(e);(f)&&;(g)==;(h)()2.下述函数重载为类Student(包括id,days,RA)的友元函数,问可能存在什么错误?istream&operator(istream&in,Student&std){doubledaily;instd.idstd.daysdaily;std.RA=std.days*daily;returnin;}3.下述函数定义存在什么错误?StudentStudent::operator=(conststring&str){id=str;returnthis;}北京大学《程序设计实习》课程3内容提要基本概念:继承、基类、派生类派生类的成员组成、可见性派生类的构造、析构派生类与基类的指针类型转换示例程序作业北京大学《程序设计实习》课程4继承与派生的概念继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。派生类是通过对基类进行修改和扩充得到的在派生类中,可以扩充新的成员变量和成员函数派生类一经定义后,可以独立使用,不依赖于基类派生类拥有基类的全部成员,包括:(private、protected、public)属性和(private、protected、public)方法在派生类的各个成员函数中,不能访问基类中的private成员北京大学《程序设计实习》课程5继承与派生的概念派生类可以定义一个和基类成员同名的成员,这叫覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::继承和派生机制大大地提高了软件的可重用性和可扩充性北京大学《程序设计实习》课程6需要继承机制的例子比如,所有的学生都有一些共同属性和方法,比如姓名,学号,性别,成绩等属性,判断是否该留级,判断是否该奖励之类的方法而不同的学生,比如中学生,大学生,研究生,又有各自不同的属性和方法,比如大学生有系的属性,而中学生没有,研究生有导师的属性,中学生竞赛、特长加分之类的属性如果为每类学生都编写一个类,显然会有不少重复的代码,浪费比较好的做法是编写一个“学生”类,概括了各种学生的共同特点,然后从“学生”类派生出“大学生”类,“中学生”类,“研究生类”classCStudent{private:charszName[20];intnAge;intnSex;public:boolIsThreeGood(){};intSetSex(intnSex_){nSex=nSex_;}voidSetName(char*szName_){strcpy(szName,szName_);}//......};classCUndergraduateStudent:publicCStudent{private:intnDepartment;public:boolIsThreeGood{......};//覆盖boolCanBaoYan(){....};}classCGraduatedStudent:publicCStudent{private:intnDepartment;charszMentorName[20];public:intCountSalary(){...};};北京大学《程序设计实习》课程9使用继承必须满足的逻辑关系如果写了一个基类A,又写了基类A的派生类B,那么要注意,“一个B对象也是一个A对象”这个命题从逻辑上成立,是A派生出B为合理派生的必要条件如上面的学生例子:一个大学生当然也是一个学生北京大学《程序设计实习》课程10使用继承容易犯的错误如果写了一个CMan类代表男人,后来又发现需要一个CWoman类来代表女人,仅仅因为CWoman类和CMan类有共同之处,就让CWoman类从CMan类派生而来,是不合理的。因为“一个女人也是一个男人”从逻辑上不成立好的做法是概括男人和女人共同特点,写一个CHuman类,代表抽象的“人”,然后CMan和CWoman都从CHuman派生classbase{intj;public:inti;voidfunc();};classderived:publicbase{public:inti;voidaccess();voidfunc();};voidderived::access(){j=5;//errori=5;//引用的是派生类的ibase::i=5;//引用的是基类的ifunc();//派生类的base::func();//基类的}derivedobj;obj.i=1;obj.base::i=1;Base::iiObj占用的存储空间北京大学《程序设计实习》课程12存取权限的继承性基类的public成员:可以被下列函数访问基类的成员函数基类的友员函数派生类的成员函数派生类的友员函数其他的函数基类的private成员:可以被下列函数访问基类的成员函数基类的友员函数基类的protected成员:可以被下列函数访问基类的成员函数基类的友员函数派生类的成员函数派生类的友员函数classFather{private:intnPrivate;//私有成员public:intnPublic;//公有成员protected:intnProtected;//保护成员};classSon:publicFather{voidAccessFather(){nPublic=1;//ok;nPrivate=1;//wrongnProtected=1;//OK,访问从基类继承的protected成员Fatherf;f.nProtected=1;//ok,访问基类对象的protected成员}}main(){Fatherf;Sons;f.nPublic=1;//Oks.nPublic=1;//Okf.nProtected=1;//errorf.nPrivate=1;//errors.nProtected=1;//errors.nPrivate=1;//error}北京大学《程序设计实习》课程15继承与派生的概念(cont.)派生类可以增加自己的属性,并且允许新的属性与基类中某个属性的名称相同。新属性与基类中某个private成员同名:在派生类的对象中只能访问新属性,访问时直接使用新属性的名称新属性与基类中某个(protected、public)成员同名:在派生类的对象既可以访问新属性,也可以访问基类中定义的属性访问新属性时直接使用新属性的名称访问基类中定义的属性时,使用作用域分辨符号:基类::属性北京大学《程序设计实习》课程16继承与派生的概念(cont.)派生类可以增加自己的方法,并且允许新的方法与基类中某个方法的名称相同。新方法与基类中某个private成员同名:在派生类的对象中只能访问新方法,访问时直接使用新方法的名称新方法与基类中某个(protected、public)成员同名,新方法覆盖了基类中原来的方法,在派生类的对象中直接使用新方法的名称进行访问:访问的是派生类中的新方法使用作用域分辨符号“基类::方法”访问:访问的是基类中定义的方法classbase{private:intb1;voidinner_fun(inti);protected:intb2;public:voidfun();};classderived:publicbase{private:intb1;voidinner_fun(inti);protected:intb2;public:voidfun();};intbase::b1intb1intbase::b2intb2derived对象的属性voidbase::inner_fun(inti)voidinner_fun(inti)voidbase::fun()voidfun()derived对象的方法在derived成员方法的实现中可以访问的成员在derived成员方法的实现中不能访问的成员classbase{private:intb1;voidinner_fun(inti);protected:intb2;public:voidfun();};classderived:publicbase{private:intb1;voidinner_fun(inti);protected:intb2;public:voidfun();};main(){baseobj;obj.b1=5;//errorobj.b2=5;//errorobj.inner_fun(5);//errorobj.fun(5);//ok}voidderived::fun(){baseobj;obj.b1=5;//errorobj.b2=5;//okbase::b2=4;//okobj.inner_fun(5);//errorbase::inner_fun(5);//errorobj.fun(5);//okbase::fun(5);//ok}北京大学《程序设计实习》课程19在利用派生类的构造函数初始化时必须首先初始化基类。classBug{private:intnLegs;intnColor;public:intnType;Bug(intlegs,intcolor);voidPrintBug();};classFlyBug:publicBug//FlyBug是Bug的派生类{intnWings;public:FlyBug(intlegs,intcolor,intwings);}派生类的构造函数北京大学《程序设计实习》课程20Bug::Bug(intlegs,intcolor){nLegs=legs;nColor=color;}//错误的FlyBug构造函数:FlyBug::FlyBug(intlegs,intcolor,intwings){nLegs=legs;//不能访问nColor=color;//不能访问nType=1;//oknWings=wings;}//正确的FlyBug构造函数:FlyBug::FlyBug(intlegs,intcolor,intwings):Bug(legs,color){nWings=wings;}表达式中可以出现:FlyBug构造函数的参数、常量FlyBugfb(2,3,4);fb.PrintBug();fb.nType=1;fb.nLegs=2;//error.nLegsisprivate在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数调用基类构造函数的两种方式显式方式:在派生类的构造函数中,为基类的构造函数提供参数derived::derived(arg_derived-list):base(arg_base-list)隐式方式:在派生类/基类的构造函数都缺省时,派生类的构造函数则自动调用基类的默认构造函数派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数派生类不继承基类的构造函数和赋值运算符,但是派生类构造函数和赋值运算符能够调用基类的构造函数和赋值运算符。FlyBugfb(2,3,4);fb.PrintBug();fb.nType=1;fb.nLegs=2;//error.nLegsisprivate#includeiostream.h
本文标题:14北京大学程序设计实习课程
链接地址:https://www.777doc.com/doc-3226359 .html