- Trong lập trình hớng đối tợng, tham số của hàm có thể là đối tợng (có kiểu lớp).
- Tuỳ theo từng trờng hợp ta có thể sử dụng 1 trong 3 cách cách truyền tham số (theo giá trị, theo con trỏ và
theo tham chiếu). Tuy nhiên giữa con trỏ và tham chiếu thì phong cách của C++ u dùng truyền theo tham chiếu hơn cả.
Ví dụ 13
Khi không có ý định thay đổi đối tợng trong hàm hoặc không muốn giữ lại đối tợng đã bị hàm làm thay đổi thì ta truyền theo tham trị
#include <iostream.h> #include <conio.h> class time {
int h,m; public:
void gettime(int h1,int m1) { h=h1;m=m1;} void puttime(void)
{cout<<h<<" hours "<<m<<" minutes." <<endl;} void sum(time,time);
};
void time::sum(time T1,time T2) { m=T1.m+T2.m; h=m/60; m=m%60; h=h+T1.h+T2.h; T1.h=T1.h+1; } void main() { time T1,T2,T3; T1.gettime(2,45); T2.gettime(3,30); T3.sum(T1,T2); T1.puttime(); T2.puttime(); T3.puttime(); getche(); } Ví dụ 14
- Khi truyền cho hàm đối là đối tợng và thấy cần thiết phải thay đổi đối tợng trong hàm thì ta truyền đối dạng địa chỉ.
- Trong ví dụ 5 dới đây minh hoạ việc truyền đối theo tham chiếu (tức là theo địa chỉ của đối tợng) để hoán đổi giá trị của 2 đối tợng của 2 lớp khác nhau qua một hàm thân thiện: void exchange(POINT&,FRAC&);
#include <iostream.h> #include <conio.h> class POINT; class FRAC { int t,m; public:
void getdata(int a,int b) { t=a; m=b;} void display() { cout<<t<<"/"<<m<<endl;} friend void exchange(POINT&,FRAC&); };
class POINT { int x,y; public:
void getdata(int a,int b) { x=a;y=b;}
void display() { cout<<"("<<x<<","<<y<<")"<<endl;} friend void exchange(POINT&,FRAC&);
};
void exchange(POINT &p,FRAC &q) { int z=p.x; p.x=q.t; q.t=z; z=p.y; p.y=q.m; q.m=z; } int main() { clrscr(); POINT a; a.getdata(5,6); FRAC b; b.getdata(2,3); cout<<"before exchange:\n" a.display(); b.display(); Kết quả before exchange: (5,6) 2/3 after exchange: (2,3) 5/6
exchange(a,b); cout<<"after exchange:\n" a.display(); b.display(); getche(); return 0; }
Bài 2. Constructor và destructor 1.1. Khái niệm chung
1. Constructor (có thể dịch là hàm khởi tạo) và destructor (hàm hủy bỏ) là 2 hàm thành phần đặc biệt của lớp.
2. Constructor là hàm thành phần cho phép khởi tạo dữ liệu của đối tợng một cách tự động khi nó đợc khai báo sau tên lớp.
3. Destructor là hàm thành phần cho phép huỷ bỏ đối tợng một cách tự động khi không còn dùng đến đối t- ợng trong chơng trình nữa.
4. Khi không khai báo constructor và destructor trong lớp thì constructor ngầm định và destructor ngầm định đợc ngầm tạo ra, đó là constructor và destructor không tham số và cũng không có nội dung (thân hàm). Theo lý thuyết, constructor ngầm định thì tự động gán dữ liệu của đối tợng bằng 0 khi đối tợng đợc tạo lập, còn destructor thì giải phóng bộ nhớ dành cho đối tợng khi thoát khỏi chơng trình.
5. Về mặt cú pháp, tên constructor phải trùng với tên lớp, tên destructor cũng phải trùng với tên lớp nhng viết sau ký hiệu "ngã" ~
1.2. Khai báo và định nghĩa constructor và destructor
a)Khai báo và phân loại
class class_name {...
class_name(); // constructor không tham số, là //constructor ngầm định
// constructor khởi tạo
class_name(class_name&); // constructor tham số đối // tợng, là constructor sao chép ~class_name(){} // Destructor,không tham số, không // nộu dung, là ngầm định.
... };
b)Định nghĩa constructor
- Vì constructor có tên gọi đặc biệt nên khai báo và định nghĩa cũng đặc biệt, không cần chỉ ra phần kiểu trả lại của hàm nh đối với những hàm thành phần khác.
class_name::class_name([arg_list]) { // nội dung constructor tơng ứng. }
Constructor với tham số truyền giá trị mặc định rất hay đợc sử dụng và nó cũng tuân theo nguyên tắc của hàm với tham số ngầm định:
- Khi constructor đợc gọi không đủ tham số thì các giá trị mặc định đợc tự động gán vào các tham số vắng mặt.
- Khi khai báo, các tham số ngầm định phải khai báo sau cùng
- Khi gọi thực hiện thì không đợc để ngắt quãng tham số.
Ngoài ra, việc tạo lập đối tợng với số lợng và thứ tự các tham số phải phù hợp với việc khai báo các constructor trong lớp.
Ví dụ
#include <iostream.h> #include <conio.h> #include <graphics.h>
#include <stdlib.h> // dung cho ham randomize #include <dos.h> // dung cho ham delay
class CIRCLE { float x,y; double r; public:
CIRCLE() {x=y=r=0;} // constructor ngam dinh CIRCLE(double r1, int x1=200, int y1=100) // constructor co tham so
{x=x1; y=y1; r=r1;} CIRCLE(CIRCLE &C) // constructor sao chep
{ x=C.x;y=C.y;r=C.r;} ~CIRCLE() {}
friend void draw(CIRCLE C) {circle(C.x,C.y,C.r);} };
void main()
{CIRCLE c1(250);
// su dung gia tri ngam dinh x=200, y=100 CIRCLE c2(350,250);
// su dung gia tri ngam dinh y=100 CIRCLE c3(300,300,100);
CIRCLE c4=CIRCLE(c1);
//dung cau tu sao chep tuong minh CIRCLE c5=c3;
// dung cau tu sao chep khong tuong minh int drive=DETECT,mode;
initgraph(&drive,&mode,"c:\\tc\\bgi"); randomize();
for(int i=0;i<10;i++)
{ setcolor(random(15)+1); draw(c1); draw(c2); draw(c3); delay(100);
setcolor(random(15)+1); draw(c4); draw(c5); delay(100);
getch(); closegraph(); }
* Nhận xét:
- Sử dụng constructor sao chép thông qua toán tử gán, và có 2 cách sử dụng: tờng minh và không tờng minh.
- Toán tử gán (=) giữa các đối tợng còn có thể xây dựng cách thứ 2 là toán tử tải bội mà ta sẽ nghiên cứu ở phần tiếp theo.
- Các constructor sẽ đợc sử dụng nhiều sau này trong các lớp có quan hệ kế thừa.
- Destructor: Đa số destructor trong lớp đơn giản không có tham số và không cần có nội dung bên trong, do đó đa số là destructor ngầm định, vậy có thể không cần khai báo destructor loại này. Tuy nhiên khi có vấn đề cấp phát động cho các đối tợng trong chơng trình thì nên có destructor để huỷ bỏ đối tợng bằng cách giải phóng bộ nhớ đã đợc cấp phát đó.
Bài 3. Toán tử tải bội và toán tử tải bội thân thiện