740 likes | 851 Views
第åç« ç»§æ‰¿å’Œæ´¾ç”Ÿç±». é¢å‘对象程åºè®¾è®¡ (C++). 10.1 引言. 10.1 引言 : 代ç é‡ç”¨çš„两ç§æœºåˆ¶. 组åˆï¼šåˆ›å»ºä¸€ä¸ªæ–°ç±»ï¼Œç±»ä¸çš„一个或多个数æ®æˆå‘˜æ˜¯ (已ç»å˜åœ¨çš„)其他类的对象。 例:计算机 = ä¸»æ¿ +CPU+ å†…å˜ + 显示器 + …. class CComputer{ Cpower Foxconn200; // 富士康 CMainBoard Intel915G;// Intel 主æ¿
E N D
第十章 继承和派生类 面向对象程序设计(C++)
10.1 引言:代码重用的两种机制 组合:创建一个新类,类中的一个或多个数据成员是 (已经存在的)其他类的对象。 例:计算机=主板+CPU+内存+显示器+ … class CComputer{ Cpower Foxconn200; // 富士康 CMainBoard Intel915G;// Intel主板 CCPU Prescott; // Prescott处理器 CMem SEC400X72C3 ; // Samsung内存条 … };
10.1 引言:代码重用的两种机制 继承:在现有的类(称为“基类” )的基础上,创 建一个新类(称为“派生类”)。派生类“共 享”基类的所有数据成员和成员函数;也可 以进一步添加自己的成员。 例:Dell C640DV 拥有C640的所有配置;此外,还 增加了支持DV的i-link 1394接口。 class C640DV: public C640{ C1394 canopusDvRex ; }
10.1.1 组合 class X { int i; public: X() { i = 0; } void set(int ii) { i = ii; } int read() const { return i; } int permute() { return i = i * 47; } }; // new class class Y { int i;
X::i Y::i 10.1.1 组合 (续) public: X x; // Y的成员是X的对象 Y() { i = 0; } void f(int ii) { i = ii; } int g() const { return i; } }; int main() { Y y; y.f(47); y.x.set(37); // 访问内嵌对象 } ///:~
10.1.2 单继承 class 派生类类名:public/private/pro…基类类名{ // 派生类的新成员; } 派生类继承基类的所有的数据成员和成员函数。除了: 1. 基类的构造函数和析构函数; 2. 基类中用户定义的new和赋值运算符 3. 基类的友元关系 访问修饰的不同,影响到派生类成员函数对基类成员的访问控制、派生类对象对基类成员的访问控制。
eg1: 基类employee class employee { private: char * name; short age; float salary; public: employee(){ name=0; age=0; salary=0.0; } employee(char * name1, short age1, float salary1) { name = new char[strlen(name1)+1];
eg1: 基类employee age=age1; salary=salary1; } void print() const{ cout<<"name:"<<name<<"age:"<<age <<"salary:"<<salary<<endl; } virtual ~employee() { delete [ ]name; } }; //~基类 employee
name age salary eg1:派生类manager1 class manager1:public employee { // 继承了基类的所有数据成员和成员函数。 }; manager1 m; m.print(); //打印manager的name,age,salary
10.1.3 多重继承(其意义尚有争议) class manager{ … }; class temporary{ … }; //类consultant(顾问) class consultant:public manager, public temporary{ … }; 当基类中含同名成员时,存在二义性的问题!
10.1.4 继承和组合的联合 class A { int i; public: A(int ii) : i(ii) { } ~A() { } void f() const { } }; class B { int i; public: B(int ii) : i(ii) { } ~B() { } void f() const { } };
10.1.4 继承和组合的联合(续) class C : public B { A a; public: C(int ii) : B(ii), a(ii) { } ~C() { } // Calls ~A() and ~B() void f() const { // Redefinition a.f(); B::f(); } }; int main() { C c(47); } ///:~
课堂作业:(10分钟) class Base { public: int i; void b(); }; class Derive:public Base{ public: void d(); }; class Member{ public: Base ba; void d(); }; 问: Derive和Member生成的对象是否具有相同的存储形式? 如果是,试从概念上、操作上,以及用途上比较两者之间的差异。
组合 vs 继承 1.概念:Member是组合,Derive是继承 2.用途:代码重用 3.操作:Base base; Derive derive; Member member; cout << base.i << derive.i << member.ba.i; base.b(); derive.b(); member.ba.b();
组合 vs 继承(续) • “Is a” relationships • Inheritance • Relationship in which a class is derived from another class • 派生类“是一个”基类 • “Has a” relationships • Composition • Relationship in which a class contains other classes as members • 类中“有一个”其他类的成员对象
10.2.1 访问控制 1. 基类对派生类的访问控制 2. 基类对派生类的对象的访问控制 3. 基类对派生类的派生类的访问控制 • class base{ • private: int i1; • protected: int j1; • private: int f1(); • }; • class drv: public base{ • private: int i2; • protected: int j2; • public: int f2(); • }; void main() { drv d1; } 问:如何访问d1的数据成员和成员函数?
公有继承 • 基类成员对派生类的可见性: --公有成员和保护成员可见,私有成员不可见。 • 基类成员对派生类对象的可见性 --公有成员可见,保护成员和私有成员不可见。 私有继承 • 基类成员对派生类的可见性: -- 公有成员和保护成员可见,私有成员不可见。 -- 所有成员对派生类的派生类的成员不可见(即转变为派生类的私有成员) • 基类成员对派生类对象的可见性 -- 所有成员都不可见
保护继承 • 基类成员对派生类的可见性: -- 公有成员和保护成员可见,私有成员不可见。 -- 公有成员和保护成员对派生类的派生类的成员可见。 • 基类成员对派生类对象的可见性 --所有成员都不可见
10.2.2 public继承 class manager2:public employee { public: int level; void m_print(){ cout << “level” << level ; // cout << age; error 不能访问基类私有成员; print(); //OK,可以访问基类公有成员; } }; manager2 m; m.print(); //OK打印manager的ame,age,salary
10.2.3 private继承 class manager3: private employee { public: int level; void m_print(){ cout << “level” << level ; //cout << age; 错!不能访问基类私有成员; print(); } }; manager2 m; m.print(); //manager3中的私有成员,不能访问
10.2.3.1 对private继承成员公有化 class Pet { public: char eat() const { return 'a'; } int speak() const { return 2; } float sleep() const { return 3.0; } float sleep(int) const { return 4.0; } }; class Goldfish : Pet { // 私有继承 public: Pet::eat; // 公有化 Pet::sleep; // 重载的成员均被公有化 };
10.2.3.1 对private继承成员公有化 int main() { Goldfish bob; bob.eat(); bob.sleep(); bob.sleep(1); //! bob.speak();// Error: private member function } ///:~
10.2.4 protected继承 class Base { int i; protected: int read() const { return i; } void set(int ii) { i = ii; } public: Base(int ii = 0) : i(ii) { } int value(int m) const { return m*i; } }; class Derived : public Base { int j;
10.2.4 protected继承 public: Derived(int jj = 0) : j(jj) {} void change(int x) { set(x); } }; int main() { Derived d; d.change(10); d.set(10); 错误,保护成员不能访问 } ///:~ 注:protected继承不常用,只是为了语言的完备性。
课堂作业:设计图形类 类的层次关系:shapeshape point 或 circle point circle cylinder cylinder
图形类 class shape { … } ; // 基类 class Point:public shape{ … }; // 点 class Circle:pulic point{ // 圆 double radius; ….; }; class Cylinder: public Circle{ // 圆柱体 double height; …. ; };
1 // Fig. 9.8: point2.h 22 // Fig. 9.8: point2.cpp 2 // Definition of class Point 23 // Member functions for class Point 3 #ifndef POINT2_H 24 #include "point2.h" 4 #define POINT2_H 25 5 26 // Constructor for class Point 6 #include <iostream> 27 Point::Point( int a, int b ) { setPoint( a, b ); } Point data members are protected to be made accessible by Circle. 28 7 8 using std::ostream; 29 // Set the x and y coordinates 30 void Point::setPoint( int a, int b ) 9 31 { 10 class Point { 32 x = a; 11 friend ostream &operator<<( ostream &, const Point & ); 12 public: 13 Point( int = 0, int = 0 ); // default constructor 14 void setPoint( int, int ); // set coordinates 15 int getX() const { return x; } // get x coordinate 16 int getY() const { return y; } // get y coordinate 17 protected: // accessible to derived classes 18 int x, y; // coordinates of the point 19 }; 20 21 #endif 1. Point definition 1.1 Function definitions
33 y = b; 34 } 35 36 // Output the Point 37 ostream &operator<<( ostream &output, const Point &p ) 38 { 39 output << '[' << p.x << ", " << p.y << ']'; 40 41 return output; // enables cascading 42 } 1.1 Function definitions
1 // Fig. 9.9: circle2.h 25 // Fig. 9.9: circle2.cpp 2 // Definition of class Circle 26 // Member function definitions for class Circle 27 #include <iomanip> 3 #ifndef CIRCLE2_H 4 #define CIRCLE2_H 28 29 using std::ios; 5 30 using std::setiosflags; 6 #include <iostream> 7 31 using std::setprecision; Circledata members are protected to be made accessible by Cylinder. 8 using std::ostream; 32 33 #include "circle2.h" 9 10 #include "point2.h" 11 12 class Circle : public Point { 13 friend ostream &operator<<( ostream &, const Circle & ); 14 public: 15 // default constructor 16 Circle( double r = 0.0, int x = 0, int y = 0 ); 17 void setRadius( double ); // set radius 18 double getRadius() const; // return radius 19 double area() const; // calculate area 20 protected:// accessible to derived classes 21 double radius; // radius of the Circle 22 }; 23 24 #endif 1. circle definition 1.1 Function definitions
34 35 // Constructor for Circle calls constructor for Point 36 // with a member initializer and initializes radius 37 Circle::Circle( double r, int a, int b ) 38 : Point( a, b ) // call base-class constructor 39 { setRadius( r ); } 40 41 // Set radius 42 void Circle::setRadius( double r ) 43 { radius = ( r >= 0 ? r : 0 ); } 44 45 // Get radius 46 double Circle::getRadius() const { return radius; } 47 48 // Calculate area of Circle 49 double Circle::area() const 50 { return 3.14159 * radius * radius; } 51 52 // Output a circle in the form: 53 // Center = [x, y]; Radius = #.## 54 ostream &operator<<( ostream &output, const Circle &c ) 55 { 56 output << "Center = " << static_cast< Point > ( c ) 57 << "; Radius = " 58 << setiosflags( ios::fixed | ios::showpoint ) 59 << setprecision( 2 ) << c.radius; 60 61 return output; // enables cascaded calls 62 } 1.1 Function definitions
1 // Fig. 9.10: cylindr2.h 2 // Definition of class Cylinder 3 #ifndef CYLINDR2_H 4 #define CYLINDR2_H 5 6 #include <iostream> 7 8 using std::ostream; 9 10 #include "circle2.h" 11 12 class Cylinder : public Circle { 13 friend ostream &operator<<( ostream &, const Cylinder & ); 14 15 public: 16 // default constructor 17 Cylinder( double h = 0.0, double r = 0.0, 18 int x = 0, int y = 0 ); 19 20 void setHeight( double ); // set height 21 double getHeight() const; // return height 22 double area() const; // calculate and return area 23 double volume() const; // calculate and return volume 24 25 protected: 26 double height; // height of the Cylinder 27 }; 28 29 #endif 1. Cylinder definition
30 // Fig. 9.10: cylindr2.cpp 31 // Member and friend function definitions 32 // for class Cylinder. 33 #include "cylindr2.h" 34 35 // Cylinder constructor calls Circle constructor 36 Cylinder::Cylinder( double h, double r, int x, int y ) 37 : Circle( r, x, y ) // call base-class constructor 38 { setHeight( h ); } Circle::area() is overidden. 39 40 // Set height of Cylinder 41 void Cylinder::setHeight( double h ) 42 { height = ( h >= 0 ? h : 0 ); } 43 44 // Get height of Cylinder 45 double Cylinder::getHeight() const { return height; } 46 47 // Calculate area of Cylinder (i.e., surface area) 48 double Cylinder::area() const 49 { 50 return 2 * Circle::area() + 51 2 * 3.14159 * radius * height; 52 } 53 54 // Calculate volume of Cylinder 55 double Cylinder::volume() const 56 { return Circle::area() * height; } 57 58 // Output Cylinder dimensions 59 ostream &operator<<( ostream &output, const Cylinder &c ) 60 { 1.1 Function definitions
61 output << static_cast< Circle >( c ) 66 // Fig. 9.10: fig09_10.cpp 62 << "; Height = " << c.height; 67 // Driver for class Cylinder 63 68 #include <iostream> 64 return output; // enables cascaded calls 69 65 } 70 using std::cout; 71 using std::endl; 72 73 #include "point2.h" 74 #include "circle2.h" 75 #include "cylindr2.h" 76 77 int main() 78 { 79 // create Cylinder object 80 Cylinder cyl( 5.7, 2.5, 12, 23 ); 81 82 // use get functions to display the Cylinder 83 cout << "X coordinate is " << cyl.getX() 84 << "\nY coordinate is " << cyl.getY() 85 << "\nRadius is " << cyl.getRadius() 86 << "\nHeight is " << cyl.getHeight() << "\n\n"; 87 88 // use set functions to change the Cylinder's attributes 89 cyl.setHeight( 10 ); 90 cyl.setRadius( 4.25 ); 91 cyl.setPoint( 2, 2 ); 1.1 Function definitions ---------------------- 1. Load headers 1.1 Initialize object 2. Function calls 2.1 Change attributes 3. Output data X coordinate is 12 Y coordinate is 23 Radius is 2.5 Height is 5.7
92 cout << "The new location, radius, and height of cyl are:\n" 93 << cyl << '\n'; 94 95 cout << "The area of cyl is:\n" 96 << cyl.area() << '\n'; circleref "thinks" cyl is a Circle, so it prints as one. pref "thinks" cyl is a Point, so it prints as one. 97 98 // display the Cylinder as a Point 99 Point &pRef = cyl; // pRef "thinks" it is a Point 100 cout << "\nCylinder printed as a Point is: " 101 << pRef << "\n\n"; 102 103 // display the Cylinder as a Circle 104 Circle &circleRef = cyl; // circleRef thinks it is a Circle 105 cout << "Cylinder printed as a Circle is:\n" << circleRef 106 << "\nArea: " << circleRef.area() << endl; 107 108 return 0; 109 } 3. Output data The new location, radius, and height of cyl are: Center = [2, 2]; Radius = 4.25; Height = 10.00 The area of cyl is: 380.53 Cylinder printed as a Point is: [2, 2] Cylinder printed as a Circle is: Center = [2, 2]; Radius = 4.25 Area: 56.74 X coordinate is 12 Y coordinate is 23 Radius is 2.5 Height is 5.7 The new location, radius, and height of cyl are: Center = [2, 2]; Radius = 4.25; Height = 10.00 The area of cyl is: 380.53 Cylinder printed as a Point is: [2, 2] Cylinder printed as a Circle is: Center = [2, 2]; Radius = 4.25 Area: 56.74 Program Output
10.3.1 继承与构造、析构函数 基类中不能被继承的部分: 1. 构造函数 2. 析构函数 3. 重载的new、赋值运算符 4. 友员关系 但是,在派生类的构造函数和重载赋值运算符中可以调用基类的构造函数和重载赋值运算符。
10.3.2 如何初始化基类成员? class manager: public employee { int level; public: manager(char * name1, short age1, float,salary1,int level1) : employee(name1,age1,salary1){ level=level1; } void m_print(){ cout<<"salary:"<<salary<<"level"<<level<<endl; print(); } }; manager m(“Maggie”,20,6000,1);
10.3.3 构造、析构函数的调用次序 创建一个派生类的对象,调用次序为: 1. 调用基类的构造函数,初始化基类成员; 2. 调用数据成员对象的构造函数(若有多个,则按类中定义的先后次序调用),初始化成员对象; 3. 调用派生类自己的构造函数;初始化派生类成员。 析构一个派生类的对象,其调用次序正好相反: 1. 调用派生类自己的析构函数; 2. 调用数据成员对象的析构函数(若有多个,则按类中定义的先后次序逆序调用); 3. 调用基类的析构函数。
10.4.1 继承与静态成员函数 • 静态成员函数可被继承到派生类中; • 如果重新定义了一个静态成员,所有在基类中的其他重载函数会被隐藏; • 如果改变了基类中一个函数的签名(signature),所有使用该函数名字的基类版本都将被隐藏。 • 与非静态成员函数的不同点: • 静态成员函数不可以是虚函数。
在派生类中,重新定义基类的成员函数。 10.6.1 重定义(redefining) // 基类 class employee { public: short age; employee( ) { age=0;} employee( short age1){ age=age1;} void print() const{ cout“ base age:"<<age<<endl; } };
// 派生类 class manager: public employee { public: int level; short age; manager(short age1,int level1) :employee(age1) { age = age1+1; level=level1; } void print(){ cout<<“derive age:"<<age; employee::print(); } };
// 主函数 void main() { manager m(24,1); employee &pref=m; pref.print() ; //调用派生类的print(); }