290 likes | 564 Views
C++ 语言程序设计. 第 5 讲: C++ 程序的结构. 构造函数. 相当于: Clock::Clock() { hour = minute = second = 0; }. 默认参数构造函数. class Clock { public : Clock() : hour(0), minute(0), second(0) {} Clock( int NewH, int NewM=0, int NewS=0); Clock(Clock& c); private :
E N D
C++语言程序设计 第5讲: C++程序的结构
构造函数 相当于:Clock::Clock() { hour = minute = second = 0; } 默认参数构造函数 class Clock { public: Clock() : hour(0), minute(0), second(0) {} Clock(int NewH, int NewM=0, int NewS=0); Clock(Clock& c); private: int hour; int minute; int second; }; Clock::Clock(int NewH, int NewM, int NewS) { hour = NewH; minute = NewM; second = NewS; } Clock::Clock(Clock& c) { hour = c.hour; minute = c.minute; second = c.second; } 拷贝构造函数
析构函数 class Clock { public: Clock(); void SetTime (int, int, int); void ShowTime(); ~Clock(); // 析构函数 private: int hour; int minute; int second; char* pbuf; }; Clock::Clock() { pbuf = new char[128]; } Clock::~Clock() { delete[] pbuf; } 对象被删除的时刻自动调用 main() { Clock c; // 构造 c.SetTime(9,30,30); } // 析构
构造函数可使每个对象都有正确的初始值 • 析构函数可使每个对象在结束时自动进行清理工作 • 析构函数的使用 • 一个类只能有一个析构函数,不允许重载! • 最先析构的是自动变量; • 静态对象的生命期一直到程序结束;(static) • 全局对象是最后被析构的。
访问函数和工具函数 • 将类的函数分为两大类,访问函数和工具函数。 • 访问函数是供客户调用的,一般为public。 • 工具函数是为实现访问函数的某些功能而需要的函数,一般为private。 • 数据成员和成员函数的使用 • 数据一般设为私有成员,便于保护; • 需要用户设置的私有数据成员用get和set提供给用户; • 要保证私有数据成员在任何时候都是有效的。
接口与实现的分离 • 软件工程的一个最基本的原则:接口与实现分离 • .h文件与.cpp文件分离 • 类的定义与类的实现分离 • 但是Java中类的定义与实现是在一起的,Why? • 因为Java中有一个专门管接口的类叫Interface。这个类是公开的,而其他类则被屏蔽起来。 这种方式称为对象的“包装”或称“封装”。
§ 控制对成员的访问 • public:, private: ( protected: ) 都是用来控制成员函数的作用。 • 类的客户可通过公有成员(public)知道类提供什么样的服务。 • 类的客户不能直接访问类的私有成员(private),它们只能通过成员函数(或友元)来访问。 • C++提倡编写与实现无关的程序。在类定义中尽量避免写函数实现的代码。(也不是绝对的,只要觉得可以公开,并以后不会改动,则可以将实现写到类的定义中) • [注意]在理论上 public:, private:protected:只使用一次。但现在C++编译器(如VC++)可支持多次。而在Java中每一个函数前面都要求加上这个说明符。
§软件的可重用性 • 面向对象的程序设计的一个重要目标就是软件的可重用性。 • 可重用性意味着以前的代码可以部分加以利用,并对其余部分改写,以免整个程序重新编写。 • 要实现可重用性,程序必须设计良好、具有较好的可读性,并预先把需要扩展部分考虑好。
函数原型(prototype)的作用域 • 函数原型中的参数,其作用域仅限于声明中。 • 例如,设有下列原型声明: double Area(double radius); • radius 的作用域仅在于此,不能用于程序正文其他地方,因而可有可无。可以写成: double Area(double); • double Area(double radius=5); • 可简化成 double Area(double =5);
块作用域 • 在块中声明的标识符,其作用域自声明处起,限于块中,例如: void fun(int a) { int b(a); cin>>b; if (b>0) { int c; ...... } } a的作用域 b的作用域 c的作用域
类作用域 • 类X的成员M具有类作用域,对M的访问方式如下: • 如果在X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以访问成员M。 class X { int M; public: void fun(); } void X::fun() { M=10; { int M=20; … … } cout << M; } 类的成员变量 局部自动变量 ?
文件作用域 • 不在前述各个作用域中出现的声明,具有文件作用域,这样声明的标识符的作用域开始于声明点,结束于文件尾。 • 全局变量! int i; // 全局变量,文件作用域 void main() { i = 5; { int i;// 局部变量,块作用域 i = 7; cout << “i=“ << i << endl;// 输出 7 } cout << “i=“ << i << endl;// 输出 5 }
例5-2: 变量的生存期与可见性 #include<iostream.h> void other(); inti=1; // i 为全局变量,具有静态生存期。 int main() { static inta;// 静态局部变量,有全局寿命,局部可见。 intb= -10; // b, c为局部变量,具有动态生存期。 intc= 0; cout <<"---MAIN---\n"; cout <<" i: "<< i<<" a: "<< a<<" b: "<< b<< " c: " <<c<<endl; c = c+8; other(); cout <<"---MAIN---\n"; cout <<" i: "<< i <<" a: "<< a<<" b: "<< b<<" c: " << c<< endl; i=i+10; other(); }
void other() { staticinta=2; staticintb=0; // a,b为静态局部变量,具有全局寿命,局部可见。 //只第一次进入函数时被初始化。 intc=10; // C为局部变量,具有动态生存期, //每次进入函数时都初始化。 a=a+2; i=i+32; c=c+5; cout<<"---OTHER---\n"; cout<<" i: "<< i<<" a: "<< a<<" b: "<< b<<" c: " << c<< endl; b=a; } 运行结果: ---MAIN--- i: 1a: 0 b: -10 c: 0 ---OTHER--- i: 33a: 4 b: 0 c: 15 ---MAIN--- i: 33a: 0 b: -10 c: 8 ---OTHER--- i: 75a: 6 b: 4 c: 15
例5-3:时钟程序 class Clock //时钟类声明 { public: //外部接口 Clock(); void SetTime(int NewH, int NewM, int NewS); // 三个形参均具有函数原型作用域 void ShowTime(); ~Clock(){} private: //私有数据成员 int Hour,Minute,Second; };
//时钟类成员函数实现 Clock::Clock() //构造函数 { Hour=0; Minute=0; Second=0; } void Clock::SetTime(int NewH,int NewM,int NewS) { Hour=NewH; Minute=NewM; Second=NewS; } void Clock::ShowTime() { cout<<Hour<<":"<<Minute<<":"<<Second<<endl; }
ClockglobClock;//声明对象globClock, //具有静态生存期,文件作用域 int main() //主函数 { cout<<"First time output:"<<endl; //引用具有文件作用域的对象: globClock.ShowTime();//对象的成员函数具有类作用域 globClock.SetTime(8,30,30); Clock myClock(globClock); //声明具有块作用域的对象myClock cout<<"Second time output:"<<endl; myClock.ShowTime(); //引用具有块作用域的对象 } 程序的运行结果为: First time output: 0:0:0 Second time output: 8:30:30
静态成员 • 静态数据成员 • 用关键字static声明 • 该类的所有对象维护该成员的同一个拷贝 • 必须在类外定义和初始化,用(::)来指明所属的类。 • 静态成员函数 • 类外代码可以使用类名和作用域操作符来调用静态成员函数。 • 静态成员函数只能引用属于该类的静态数据成员或静态成员函数。
静态数据成员声明 例5-4: 具有静态数据成员的Point类 class Point { public: Point(int xx=0, int yy=0) { X=xx; Y=yy; countP++; } Point(Point &p); int GetX(){return X;} int GetY(){return Y;} void GetC() { cout <<" Object id = " << countP << endl; } private: int X,Y; staticint countP; };
静态数据成员初始化 Point::Point(Point &p) { X=p.X; Y=p.Y; countP++; } int Point::countP=0; int main() { Point A(4,5); cout<<"Point A,"<<A.GetX()<<","<<A.GetY(); A.GetC(); Point B(A); cout<<"Point B,"<<B.GetX()<<","<<B.GetY(); B.GetC(); }
静态成员函数举例 class Application { public: staticvoid f(); staticvoid g(); private: staticint global; }; int Application::global=0; void Application::f() { global = 5; } void Application::g() { cout << global << endl; } int main() { Application::f(); Application::g(); return 0; }
友元 friend • 友元是C++提供的一种破坏数据封装和数据隐藏的机制。 • 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。 • 可以使用友元函数和友元类。 • 为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
例5-6: 使用友元函数计算两点距离 class Point //Point类声明 { public: //外部接口 Point(int xx=0, int yy=0) {X=xx;Y=yy;} int GetX() { return X; } int GetY() { return Y; } friendfloat fDist (Point &a, Point &b); private: //私有数据成员 int X,Y; }; float fDist ( Point& a, Point& b ) { double dx = a.X-b.X; double dy = a.Y-b.Y; return (float)sqrt(dx*dx+dy*dy); } int main() { Point myp1(1.0f, 1.0f), myp2(4.0f, 5.0f); cout<<"The distance is “; cout<< fDist (myp1, myp2) << endl; return 0; }
class A { friend class B; public: void Display() { cout << x << endl; } private: int x; } classB { public: void Set(int i); void Display(); private: A a; }; voidB::Set(int i) { a.x = i; } voidB::Display() { a.Display(); } 友元类 访问A的私有成员 但在 A 中不能访问 B 的私有成员。!!!
常类型 const • 常类型是只读的意思。 • 常类型的对象必须进行初始化,而且不能被更新。 • 常引用:被引用的对象不能被更新。 const 类型说明符 &引用名 • 常对象:必须进行初始化,不能被更新。 类名 const 对象名
常对象举例 class A { public: A(int i,int j) { x = i; y = j; } ... private: int x,y; }; A const a(3,4); //a是常对象,不能被修改
例5-8: 常成员函数举例 class R { public: R(int r1, int r2){ R1 = r1; R2 = r2; } void print(); void print() const; private: int R1,R2; }; void R::print() { cout<<R1<<":"<<R2<<endl; } void R::print() const { cout<<R1<<";"<<R2<<endl; } int main() { R a(5,4); a.print(); //调用void print() const R b(20,52); b.print(); //调用void print() const }
例5-9: 常数据成员举例 int main() {/*建立对象a和b,并以100和0作为初值, 分别调用构造函数,通过构造函数的初始化 列表给对象的常数据成员赋初值*/ A a1(100),a2(0); a1.print(); a2.print(); } class A {public: A(int i); void print(); constint& r; private: constint a; staticconstintb; //静态常数据成员 }; constint A::b=10; A::A(int i): a(i), r(a) {} void A::print() { cout<<a<<":"<<b<<":"<<r<<endl; } 运行结果: 100:10:100 0:10:0