Hàm thành viờn
Cỏc khai bỏo một thành viờn là hàm trong một lớp cú thể dựng hai dạng chớnh sau: Mụ tả trực tiếp trong định nghió của lớp mà hàm là thành viờn [virtual] int Mụ tả sau bờn ngoài sau định nghĩa của lớp mà hàm là thành viờn
Hàm khởi dựng (cũn gọi là cấu tử, Constructor)
Cấu tử là hàm thành viờn đặc biệt, cú tờn trựng với tờn của lớp, làm nhiệm vụ tạo lập đối tượng theo yờu cầu. Khi một đối tượng được khai bỏo thỡ cấu tử sẽ tự động thực hiện để tạo lập đối tượng trong bộ nhớ. Một lớp cú thể cú nhiều cấu tử (tải bội), cấu tử khụng cú tham số là cấu tử mặc định. Nếu ta khụng định nghĩa một cấu tử nào thỡ cấu tử mặc định sẽ được sử dụng, trỏi lại sẽ khụng được sử dụng.
Vớ dụ: #include <iostream.h> class Point { int x, y; public: Point(){x=0;y=0;} //Cấu tử mặc định Point(int a, int b=0){x=a;y=b;}
void Display(){cout <<"Toa do: ("<<x<<','<<y<<')'<<endl; }; int main(){ Point a, b(1), c(2,3); a.Display();b.Display();c.Display(); return 0; }
Đối tượng a được tạo lập bởi cấu tử thứ nhất. Cỏc đối tượng b và c được tạo lập bởi cấu tử thứ hai. Cấu tử thứ hai cú một tham số mặc định, nếu ta đặt mặc định cho cả hai tham số thỡ phải bỏ đi cấu tử thứ nhất, vỡ nếu khụng, sẽ dẫn đến sự nhập nhằng khi khai bỏo Point a; (Ambiguity between 'Point::Point()' and 'Point::Point(int,int)').
Hàm hủy (cũn gọi là huỷ tử, destructor)
Khi đối tượng khụng cũn được sử dụng thỡ nờn giải phúng nú khỏi bộ nhớ. Cỏc huỷ tử được sử dụng để làm việc này. Huỷ tử cũng là hàm thành viờn cú tờn trựng với tờn của lớp, nhưng cú thờm ký tự ~ ở trước. Nếu ta khụng định nghĩa, thỡ sử dụng huỷ tử mặc định.Tất cả cỏc huỷ tử đều khụng cú tham số. Trong vớ dụ trờn, ta cú thể định nghĩa huỷ tử như sau:
Hàm hằng
Một hàm thành viờn cú thể được khai bỏo đẻ trở thành hàm hằng. Với khai bỏo này thỡ thành viờn đú sẽ chỉ cú giỏ trị đọc đuợc mà khụng cú hiệu lực thay đổi giỏ trị nội tại của một thực thể. Để khai bỏo thỡ từ khúa const phải được đặt ngay sau khai bỏo hàm và đứng trước khối mó xỏc lập hàm số đú nếu cú.
Lưu ý: Trong trường hợp này thỡ hàm hằng được hiểu với ý nghĩa khỏc với ý nghĩa trong toỏn học (khi một hàm số là cú giỏ trị khụng đổi)
// hàm hằng class Time {
public:
Time(int h, int m, int s);
int getHour() const; //Hàm hằng chỉ đọc được
void setHour(int h); // hàm này dựng để gỏn hay thay đổi giỏ trị nờn khụng thể khai bỏo const
private: int hour; };
int Time::getHour() const //khai bỏo hàm hằng {
return hour; // khụng thay đổi giỏ trị của bất kỡ thành viờn nào }
void Date::setHour(int h) {
hour = h; // thay đổi giỏ trị của thành viờn dữ liệu }
int main() {
Time MyTime(10,5,15); const Time MyNoon(12,0,0);
// Khai bỏo một đối tượng (hay một thực thể) là hằng MyTime.setHour(5); // OK
Mytime.getHour(); // OK // MyNoon.setHour(2);
// lỗi dũng này vỡ việc cài giỏ trị mới lờn hằng MyNoon }
Lưu ý: Việc khai bỏo một kiểu dữ liệu núi chung hay một thành viờn của một lớp núi riờng là một hằng cú thể cú nhiều điểm phức tạp khi kết hợp với cỏc kiểu tham chiếu hay con trỏ. (xem thờm Const Correctness in C++, The C++ 'const' Declaration: Why & How và en:Const correctness về cỏch sử dụng từ khúa const cho cú hiệu quả)
Hàm ảo (virtual function)
Hàm ảo thường được định nghĩa ở lớp cơ sở và cho phộp cỏc lớp dẫn xuất từ nú được định nghĩa lại hàm ảo này. Giả sử FIGURE là lớp cơ sở, cú sẵn phương thức Draw(). Lớp SQUARE và lớp CIRCLE cựng được dẫn xuất từ lớp FIGURE. Tất nhiờn ta sẽ phải định nghĩa lại hai phương thức là SQUARE::Draw() và CIRCLE::Draw() của từng lớp cho phự hợp. Giả sử ptr là con trỏ trỏ đối tượng thuộc lớp FIGURE, s và c tương ứng là hai đối tượng thuộc lớp SQUARE và CIRCLE. Ta muốn rằng, khi cho ptr trỏ tới s thỡ lời gọi prt->Draw() sẽ vẽ ra hỡnh vuụng, cũn khi cho ptr trỏ tới c thỡ lời gọi prt- >Draw() sẽ vẽ ra hỡnh trũn. Cỏch giải quyết của C++ là định nghĩa FIGURE::Draw() là hàm ảo. Ta xem vớ dụ sau: #include <iostream.h> #include <conio.h> class FIGURE { int Xc, Yc; public:
FIGURE (int x=0, int y=0){Xc=x;Yc=y;}
virtual void Draw(){ cout<<"Tam tai: ("<<Xc<<','<<Yc<<')'<<endl;} };
class CIRCLE:public FIGURE { int R;
public:
CIRCLE (int x=0, int y=0, int r): FIGURE(x,y) {R =r;} void Draw()
{ cout <<"Ve hinh Tron:"<<endl; FIGURE::Draw();
cout<<"Ban kinh: "<<R<<endl; }
};
class SQUARE:public FIGURE { int D;
SQUARE (int x=0, int y=0, int d): FIGURE(x,y) {D=d;} void Draw()
{ cout <<"Ve hinh Vuong:"<<endl; FIGURE::Draw();
cout<<"Do dai canh: "<<D<<endl; } }; int main() { SQUARE s(20,20,40); CIRCLE c(30,30,50); FIGURE *ptr; ptr=&s; ptr->Draw(); ptr=&c; ptr->Draw(); getch(); return 0; }
Khụng thể định nghĩa cấu tử là hàm ảo, vỡ chớnh cấu tử làm nhiệm vụ khởi tạo bảng phương thức ảo VMT (Virtual Methods Table). Tuy nhiờn cỏc huỷ tử cú thể là hàm ảo.
BÀI TẬP
BÀI TẬP MẪU
Bài 1: C++ chấp nhận hai kiểu chỳ thớch. Cỏc lập trỡnh viờn bằng C đó quen với cỏch chỳ thớch bằng /*…*/. Trỡnh biờn dịch sẽ bỏ qua mọi thứ nằm giữa /*…*/.
Xột chương trỡnh sau : /*Chương trỡnh in cỏc số từ 0 đến 9.*/ #include <iostream.h> void main() { int i; for(i = 0; i < 10 ; ++ i) // 0 - 9 cout<<i<<"\n"; // In ra 0 - 9 }
Núi chung, kiểu chỳ thớch /*…*/ được dựng cho cỏc khối chỳ thớch lớn gồm nhiều dũng, cũn kiểu // được dựng cho cỏc chỳ thớch một dũng.
Bài 2:Chương trỡnh nhập vào hai số. Tớnh tổng và hiệu của hai số vừa nhập. #include <iostream.h>
void main() {
int x, y;
cout<< "Nhap vao mot so x:"; cin>>x;
cout<< "Nhap vao mot so y:"; cin>>y;
cout<<"Tong cua chung:"<<x+y<<"\n"; cout<<"Hieu cua chung:"<<x-y<<"\n"; }
Bài 3: Sử dụng toỏn tử xuất nhập để viết thực đơn cho chương trỡnh: #include <iostream.h>
void menu() {
cout<<”Menu \n”; cout<<”1. Cong viec 1\n”; cout<<”2. Cong viec 2\n”; cout<<”3. Cong viec 3\n”;
cout<<”4. Ket thuc chuong trinh \n\n”; }
void main() {
int lc; do {
// viet menu len man hinh menu();
//lay lua chon
cout<<”Ban hay chon cong viec can thuc hien:1->4”;cin>>lc; switch(lc){
case 1 : cout<<”Thuc hien cong viec 1\n”; break; case 2 : cout<<”Thuc hien cong viec 2\n”; break; case 3 : cout<<”Thuc hien cong viec 3\n”; break; }
//lap cho den khi nguoi su dung lua chon 4 } while(lc!=4);
}
Bài 4: Tỡm lỗi sai của đoạn chương trỡnh sau: int n;
cin>>n;
cin>>a[i];
} for(i=0;i<n;i++) cout<<a[i];
Lời gải
Chương trỡnh bị lỗi trong vũng for thứ hai do biến mảng a khụng được định nghĩa. Mảng a được khai bỏo trong vũng for thứ nhất chỉ cú tầm hoạt động trong vũng for đú mài thụi. Do vậy, chương trỡnh khụng thể biết ở trong vũng lặp for thứ hai. Chỳ ý biến nguyờn i được khai bỏo trong dũng lệnh for cú vị trớ tương đương với việc khai bỏo i ở bờn ngoài for. Vỡ vậy, trong vũng for thứ hai ta sử dụng biến i nhưng chương trỡnh khụng bỏo lỗi.
Bài 5: Tỡm lỗi sai cho cỏc khai bỏo prototype hàm dưới đõy (cỏc hài này được khai bỏo trong cựng một chương trỡnh)
int func1(int); // (1)
float func1(int); // (2)
int func1(float); //(3)
void func1(int = 0,int); //(4) void func2(int,int = 0); //(5)
void func2(int); //(6)
void func2(float); //(7)
Lời gải:
Trong định nghĩa chồng hàm, trỡnh biờn dịch phõn biệt cỏc hàm bởi kiểu dữ liệu trả ra của hàm mà chỉ phõn biệt bởi danh sỏch tham số của hàm. Do vậy hàm 1 và hàm 2 bị định nghĩa chồng lờn nhau và trỡnh biờn dịch bỏo lỗi. Giữa hàm 2 và hàm 3 khụng cú lỗi bởi chỳng khỏc nhau bởi kiểu dữ liệu của tham số. Trong hàm 4 ta đó sử dụng sai cỏch truyền giỏ trị mặc định cho tham số. Khụng bỏo giờ truyền giỏ trị mặc định cho một tham số trước một tham số khụng được truyền giỏ trị ngầm định.
Trong cỏch định nghĩa hai hàm 5 và 6 cú sự nhập nhằng. Khi ta gọi hàm func2 với tham số là một số nguyờn thỡ trỡnh biờn dịch khụng biết là sẽ gọi hàm 5 hay hàm 6 bởi vỡ cả hai hàm này đều được.Trong trường hợp này trỡnh biờn dịch cũng thụng bỏo lỗi.
Bài 6: Tỡm lỗi sai(lỗi cỳ phỏp và bộ nhớ) cho chương trỡnh sau: int & refl ( ) {
int a=5; return a; }
int & ref2(int a){ a++;
return a; }
int & ref3(int & a) { a++; return a; } int a=5; int &r1; int &r2=22; int &r3=a; int &r4=ref3(5); int &r5=ref3(a); Trả lời:
Trong cỏc hàm cú kết quả trả về là một tham chiếu, chỳng ta luụn phải chỳ ý rằng biến được trả lại cú giỏ trị là tham chiếu khụng bị xoỏ khoải bộ nhớ chương trỡnh khi kết thỳc thực hiện hàm. Do vậy hai hàm ref1 và ref2 là sai bởi vỡ nú trả về tham chiếu tới biờn mà a lại là biến cục bộ trong ref1 và là tham số trong ref2 chỉ được tạo ra tạm thời trờn stack khi gọi hàm và xoỏ khỏi stack khi
Trong khỏi bỏo cỏc tham chiếu phải được gắn với một biến nào đú trong bộ nhớ. Do vậy cỏc khai bỏo r1, r2 là sai. Lời gọi ref3(5) cũng là sai bởi vỡ tham số cho hàm phải là tham chiếu đến một biến, trong khi đú ta lại truyền vào hằng số.
Bài7: Cho biết kết quả thực hiện chương trỡnh sau: #include <iostream.h>
int & foo(int &a,int b) { b+=a; if (b>5) a++; return a; } void main() {
int i=2,j=4; int k=foo(i,j); k++; cout<<i<<” “<<j<<” “<<k<<endl; int &l=foo(i,j); l++ ; cout<<i<<” “<<j<<” “<<l<<endl; } Lời gải:
Trong chương trỡnh trờn cần chỳ ý hai điểm. Thứ nhất là ta truyền vào cho hàm tham chiếu của biến i chứ khụng phải biến i. Do vậy, mọi thay đổi của tham số này trong hàm là thay đổi tới biến i được tham chiếu tới. Tương tự như vậy với tham chiếu
l. Tham chiếu l được xỏc lập bằng tham chiếu trả ra của hàm chớnh là tham chiếu tới biến i. Do vậy mọi thay đổi l chớnh là thay đổi i.
Bài 8: Viết một hàm hoan vi dựng để hoỏn vị hai số nguyờn. Sau đú viờt chương trỡnh nhập và sắp xếp một mảng số nguyờn.
Trả lời:
#include <iostream.h> void hoanvi(int &a,int &b) {
int tam=a; a=b; b=tam; }
void main() {
// Nhap du lieu int n;
cout<<” Ban hay cho so phan tu cua mang n=”;cin>>n; //Cap phat bo nho cho mang int *a=new int(n);
cout<<”\n Hay nhap gia tri cho cac phan tu cua mang \n”; for(int i=0;i<n;++i)
{
cout<<”a[“<<i<<”]=”;cin>>a[i]; }
// Sap xep for(i=0;i<n-1;i++) for(int j=i++;j<n;j++)
if (a[i]>a[j]) hoanvi(a[i],a[j]); // In ket qua
cout<<”\n Cac phan tu cua mang sau khi da sap xep la \n”; for(i=0;i<n;i++)
cout<<a[i]<<” “; delete a;
}
Bài 9: Chương trỡnh tạo một mảng động, khởi động mảng này với cỏc giỏ trị ngẫu nhiờn và sắp xếp chỳng.
#include <iostream.h> #include <time.h> #include <stdlib.h> void main()
int N;
cout<<"Nhap vao so phan tu cua mang:"; cin>>N;
int *P=new int[N]; if (P==NULL)
{
cout<<"Khong con bo nho de cap phat\n"; }
for(int i=0;i<N;++i)
P[i]=rand()%100; //Tạo cỏc số ngẫu nhiờn từ 0 đến 99 cout<<"Mang truoc khi sap xep\n";
for(i=0;i<N;++i) cout<<P[i]<<" "; for(i=0;i<N-1;++i)
for(int j=i+1;j<N;++j) if (P[i]>P[j]) {
int Temp=P[i]; P[i]=P[j]; P[j]=Temp; }
cout<<"\nMang sau khi sap xep\n"; for(i=0;i<N;++i) cout<<P[i]<<" ";
delete []P; }
Bài 10: Chương trỡnh cộng hai ma trận trong đú mỗi ma trận được cấp phỏt động. Mảng hai chiều cú thể xem như mảng một chiều. Gọi X là mảng hai chiều cú kớch thước m dũng và n cột. A là mảng một chiều tương ứng. Nếu X[i][j] chớnh là A[k] thỡ k = i*n + j
Chỳng ta cú chương trỡnh như sau : #include <iostream.h>
#include <conio.h> //prototype
void AddMatrix(int * A,int *B,int*C,int M,int N); int AllocMatrix(int **A,int M,int N);
void FreeMatrix(int *A);
void InputMatrix(int *A,int M,int N,char Symbol);
void DisplayMatrix(int *A,int M,int N); int main()
{
int M,N;
int *A = NULL,*B = NULL,*C = NULL; clrscr();
cout<<"Nhap so dong cua ma tran:"; cin>>M;
cout<<"Nhap so cot cua ma tran:"; cin>>N;
//Cấp phỏt vựng nhớ cho ma trận A if (!AllocMatrix(&A,M,N))
{
cout<<"Khong con du bo nho!"<<endl; return 1;
}
//Cấp phỏt vựng nhớ cho ma trận B if (!AllocMatrix(&B,M,N))
{
cout<<"Khong con du bo nho!"<<endl; FreeMatrix(A);//Giải phúng vựng nhớ A return 1; } //Cấp phỏt vựng nhớ cho ma trận C if (!AllocMatrix(&C,M,N)) {
FreeMatrix(A);//Giải phúng vựng nhớ A FreeMatrix(B);//Giải phúng vựng nhớ B return 1;
}
cout<<"Nhap ma tran thu 1"<<endl; InputMatrix(A,M,N,'A');
cout<<"Nhap ma tran thu 2"<<endl; InputMatrix(B,M,N,'B');
clrscr();
cout<<"Ma tran thu 1"<<endl; DisplayMatrix(A,M,N);
cout<<"Ma tran thu2"<<endl; DisplayMatrix(B,M,N);
AddMatrix(A,B,C,M,N);
cout<<"Tong hai ma tran"<<endl; DisplayMatrix(C,M,N); FreeMatrix(A); //Giải phúng vựng nhớ A FreeMatrix(B);//Giải phúng vựng nhớ B FreeMatrix(C);//Giải phúng vựng nhớ C return 0; } //Cộng hai ma trận
void AddMatrix(int *A,int *B,int*C,int M,int N) {
for(int I=0;I<M*N;++I) C[I] = A[I] + B[I]; }
//Cấp phỏt vựng nhớ cho ma trận int AllocMatrix(int **A,int M,int N) {
*A = new int [M*N]; if (*A == NULL) return 0; return 1;
}
//Giải phúng vựng nhớ void FreeMatrix(int *A) {
if (A!=NULL) delete [] A; }
//Nhập cỏc giỏ trị của ma trận
void InputMatrix(int *A,int M,int N,char Symbol) { for(int I=0;I<M;++I) for(int J=0;J<N;++J) { cout<<Symbol<<"["<<I<<"]["<<J<<"]="; cin>>A[I*N+J]; } } //Hiển thị ma trận
void DisplayMatrix(int *A,int M,int N) {
for(int I=0;I<M;++I) {
for(int J=0;J<N;++J) {
out.width(7);//Hien thi canh le phai voi chieu dai 7 ky tu cout<<A[I*N+J]; }
cout<<endl; }
} //End.
Bài 11: Giả sử cú cỏc lớp như trong khai bỏo. Chỉ ra cỏc lỗi sai cho cỏc lệnh của chương trỡnh viết dưới đõy.
class A {
public: void func(); };
class B: private class A { }
A a; B b; a.func();
A* pA =&b; B* pB=&a;
Lời giải
Lời gọi b.func() cú lỗi bởi vỡ lớp B kế thừa lớp A theo chế độ private. Do vậy, tất cả cỏc thành phần của A sẽ là private trong B, hơn nữa khụng thể truy nhập vào một thành phần private.
Một đối tượng của lớp dẫn xuất cũng cú thể coi là đối tượng của lớp cơ sở. Do vậy, khia bỏo A* pA=&b là hoàn toàn đỳng. Nhưng điều ngược lại là khụng đỳng, nờn khai bỏo B* pB=&a sẽ gõy lỗi khi biờn dịch.
BÀI TẬP TỰ GIẢI
I. Bài tập Cõu lệnh đơn
1. Viết chương trỡnh in lờn màn hỡnh một thiệp mời dự sinh nhật cú dạng: THIEP MOI
Thõn mời bạn : Nguyễn Mạnh Hựng Tới dự lễ sinh nhật của mỡnh
Vào lỳc 19h ngày 12/10/2005 Tại 05/42 Trần Phỳ - Cần Thơ Rất mong được đún tiếp !
Hồ Thu Hương *******************************************
2. Viết chương trỡnh nhập vào bỏn kớnh r của một hỡnh trũn. Tớnh chu vi và diện tớch của hỡnh trũn theo cụng thức :
Chu vi CV = 2*Pi*r
Diện tớch S = Pi*r*r
In cỏc kết quả lờn màn hỡnh
3. Viết chương trỡnh nhập vào độ dài 3 cạnh a, b, c của một tam giỏc. Tớnh chu vi và diện tớch của tam giỏc theo cụng thức:
Chu vi CV = a+b+c
Diện tớch S = sqrt(p*(p-a)*(p-b)*(p-c)) Trong đú: p=CV/2
In cỏc kết quả lờn màn hỡnh
4. Viết chương trỡnh tớnh logax với a, x là cỏc số thực nhập vào từ bàn phớm, và x>0, a>0, a != 1.( dựng logax=lnx/lna)
5. Viết chương trỡnh nhập vào tọa độ của hai điểm (x1, y1) và (x2, y2)
a) Tớnh hệ số gúc của đường thẳng đi qua hai điểm đú theo cụng thức: Hệ số gúc = (y2 - y1) /(x2 - x1).
b) Tớnh khoảng cỏch giữa hai điểm theo cụng thức: 6. Viết chương trỡnh nhập vào một ký tự:
a) In ra mó Ascii của ký tự đú. b) In ra ký tự kế tiếp của nú.
1/R=1/R1 + 1/ R2 + 1/ R3
8. Viết chương trỡnh nhập vào điểm ba mụn Toỏn, Lý, Húa của một học sinh. In ra điểm trung bỡnh của học sinh đú với hai số lẻ thập phõn.
9. Viết chương trỡnh nhập vào ngày, thỏng, năm. In ra ngày thỏng năm theo dạng dd/mm/yy. (dd: ngày, mm: thỏng, yy : năm. Vớ dụ: 20/11/99 )
10. Viết chương trỡnh đảo ngược một số nguyờn dương cú đỳng 3 chữ số.
II. Bài tập Cõu lệnh cú cấu trỳc
1. Viết chương trỡnh nhập 3 số từ bàn phớm, tỡm số lớn nhất trong 3 số đú, in kết quả lờn màn hỡnh. 2. Viết chương trỡnh tớnh chu vi, diện tớch của tam giỏc với yờu cầu sau khi nhập 3 số a, b, c phải kiểm tra lại xem a, b, c cú tạo thành một tam giỏc khụng? Nếu cú thỡ tớnh chu vi và diện tớch. Nếu khụng thỡ in ra cõu " Khụng tạo thành tam giỏc".
3. Viết chương trỡnh giải phương trỡnh bậc nhất ax+b=0 với a, b nhập từ bàn phớm.
4. Viết chương trỡnh giải phương trỡnh bậc hai ax2+bx + c = 0 với a, b, c nhập từ bàn phớm.