C++类与对象

1. 问题

传统C语言的做法:

   1 struct complex {
   2     double real;
   3     double imag;
   4 };
   5 complex add(complex a, complex b); 
   6 complex substract(complex a, complex b);
   7 complex multiply(complex a, complex b);

   1 struct Clock {
   2     int second;
   3     int minute;
   4     int hour;
   5 };
   6 void SetTime( Clock *c, int h, int m, int s) {
   7     c->second = s;
   8     c->minute = m;
   9     c->hour = h;
  10 }
  11 void ShowTime( Clock *c) {
  12     cout << c->hour << c->minute << c->second;
  13 }

2. C++的类

   1 struct Clock {
   2     int  hour, minute, second;
   3     void SetTime(int h, int m, int s);
   4     void ShowTime();
   5 };

C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。类中的函数又被称为方法、成员函数。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中:

   1 struct Clock {
   2     int  hour, minute, second;
   3     void SetTime(int h, int m, int s) {
   4         hour = h; minute = m; second = s;
   5     }
   6     void ShowTime() {
   7         cout << hour << minute << second;
   8     }
   9 };

也可以分开定义,比如:

   1 //clock.h头文件:
   2 struct Clock {
   3     int  hour, minute, second;
   4     void SetTime(int h, int m, int s);
   5     void ShowTime();
   6 };
   7 
   8 //clock.cpp源文件:
   9 void Clock::SetTime(int h, int m, int s) {
  10     hour = h;
  11     minute = m;
  12     second = s;
  13 }
  14 void Clock::ShowTime() {
  15     cout << hour << minute << second;
  16 }

对象的定义和访问方式类似于结构体

   1 int main() {
   2    struct Clock now;      // 类似于C语言结构体
   3    Clock next;            // struct可以省略
   4    next.hour = 0;         // 同C语言结构体类似,可以访问成员
   5    now.SetTime(9, 40, 0); // 可以用类似的方式调用成员函数
   6    now.ShowTime();
   7    next.SetTime(9, 40, 1);
   8    next.ShowTime();
   9 }

在堆空间分配和访问对象

   1 void f() {
   2     Clock * my_clock = new Clock; //分配对象
   3     my_clock->hour = 10;          //通过指针访问成员
   4     my_clock->SetTime (9, 40, 0); //通过指针调用成员函数
   5     my_clock->ShowTime();
   6     delete my_clock;              //释放对象
   7 }

定义对象数组

   1 int main() {
   2     Clock clocks[100]; // 类似于int array[100];
   3     clocks[0].SetTime(9, 10, 25);
   4     clocks[1].SetTime(9, 9, 13);
   5     clocks[2].SetTime(9, 12, 25);
   6     // ... ...
   7     Clock *pc = new Clock[100];
   8     pc[0].SetTime(11, 20, 30);
   9     // ... ...
  10 }

成员函数和普通函数一样可以内联、重载、带默认值。函数体写在类定义内部的成员函数,默认就是内联的。

   1 struct Clock {
   2     int second, minute, hour;
   3     inline void ShowTime() { //inline may be omitted
   4         cout << hour << minute << second;
   5     }
   6 };

类外定义函数体的成员函数,要定义成内联需要加inline关键字,并把函数体写在头文件中

   1 inline void Clock::ShowTime() {
   2     cout << hour << minute << second;
   3 }

   1 struct Clock {
   2     int second, minute, hour;
   3     void SetTime(int h, int m) {
   4         hour = h;  minute = m; second = 0;
   5     }
   6     void SetTime(int h) {
   7         hour = h;  minute = second = 0;
   8     }    
   9 };
  10 int main() {
  11     Clock t;
  12     t.SetTime( 10, 30);
  13     t.SetTime( 10 );
  14 }

   1 struct Clock {
   2     int second, minute, hour;
   3     void SetTime(int h =0, int m=0, int s=0);
   4 };
   5 void Clock::SetTime(int h, int m, int s) {
   6     hour = h;  minute = m; second = s;
   7 }

3. 类的访问权限

   1 class Clock {
   2 private: //只能在类内访问
   3         int hour, minute, second;  
   4 public:  //可以在类外访问
   5         void SetTime(int yy, int mm, int dd);
   6         void ShowTime();
   7 };

公有部分定义了类的外部接口,可供类的使用者调用。私有部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是封装。private和public为新增的关键字。

   1 int main(){
   2     Clock d;
   3     d.SetTime(8, 27, 0);  // 访问public, ok
   4     d.hour = 10;          // 访问private错误
   5 }
   6 void Clock::SetTime(int h, int m, int s) {
   7     hour = h;    //访问private, ok
   8     minute = m;
   9     second = s;
  10 }

struct与class的访问权限的区别:struct默认public,class默认为private

   1 struct ClockA {
   2     void SetTime(int yy, int mm, int dd); // public here
   3 };
   4 
   5 class ClockB {
   6     int hour, minute, second; // private here;
   7 };

4. 构造函数constructor

定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。

   1 int i = 5;  // 定义普通变量,定义的同时初始化
   2 cout << i;  // 访问变量
   3 Clock time; // 定义对象,定义了以后没有初始化
   4 time.ShowTime();          //错误,没有初始化
   5 time.SetTime(2000, 5, 1); //需要调用初始化函数后
   6 time.ShowTime();          //才能够访问
   7 

为避免偶然使用没有初始化的对象的错误,可以在类中定义一个特殊的函数——构造函数。它在对象被定义时,就被自动调用,以确保完成初始化。

   1 class Clock{
   2 public:
   3     Clock(int h, int m, int s);
   4 };

构造函数的名字与类名相同,没有返回类型。它利用特定的值构造对象,把对象初始化为一个特定的状态。

   1 Clock::Clock( int h, int m, int s) {
   2     hour = h; minute = m; second = s;
   3 }

带构造函数的对象定义

   1 int main() {
   2     Clock now;  //错误
   3     Clock getup(6, 30, 30); 
   4     Clock now = Clock(9, 21, 20);
   5     Clock *pC = new Clock(23, 30, 25);
   6 }

构造函数可以重载,也可以有缺省参数:

   1 class Clock {
   2     int hour, minute, second;
   3 public:
   4     Clock(int h, int m=0, int s=0);
   5     Clock(); // default constructor默认构造函数
   6     Clock(const char *);
   7     void Clock (/*...*/); // error
   8 };

各个构造函数所需要参数不同,但构造函数都没有返回类型。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。

   1 int main() {
   2     Clock now;  //默认构造now对象call default constructor
   3     Clock getup(6, 30, 30); 
   4     Clock now = Clock(9, 21);
   5     Clock *pC = new Clock("23:30:0");
   6 }

一个类要能够默认构造对象,需要:这个类一个构造函数都没有写(系统会自动生成一个默认构造函数),或者至少写有一个默认构造函数。一个所有参数具有默认参数的构造函数也是默认构造函数,比如

   1 class Clock {
   2     int hour, minute, second;
   3 public:
   4     Clock(int h =0, int m = 0, int s=0);
   5 };

对象被摧毁时,一个成员函数也会自动被调用,这个函数称为称为析构函数,一般完成资源的释放工作

   1 class Clock {
   2         int hour, minute, second;
   3 public:
   4         ~Clock();
   5 };

析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。例如:

   1 int main() {
   2     Clock d1(4, 11, 1);
   3     if( true ){
   4         Clock *d2 = new Clock(9, 9, 9);
   5         Clock d3(8, 10,0);
   6         //…
   7     } // d3 is destructed here
   8     delete d2; // d2 is destructed here
   9     Clock d4;
  10 }  // d1,d4 is destructed here
  11 

new会自动调用构造函数,而malloc不能。delete会自动调用析构函数,而free不能。

   1 int main() {
   2     Clock *d1 = new Clock ;
   3     Clock *d2 = new Clock ( 10, 11, 1);
   4     Clock *d3 = new Clock [ 1000 ];
   5     Clock *d4 = (Clock *)malloc( sizeof(Clock) );
   6     delete d1;
   7     delete d2;
   8     delete[ ] d3;
   9     free(d4);
  10 }

要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:

   1 void f() {
   2     Clock cls[10];
   3     Clock *p = new Clock[10];
   4 }
ch3n2k.com | Copyright (c) 2004-2020 czk.