intTypePromotion=3

chương 2

Chia sẻ: Huongdanhoctot_4 Huongdanhoctot_4 | Ngày: | Loại File: PDF | Số trang:30

0
33
lượt xem
2
download

chương 2

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Biến tham chiếu Trong C++ cho phép sử dụng loại biến thứ ba là biến tham chiếu. So với 2 loại biến quen biết nói trên, thì biến này có những đặc điểm sau: + Biến tham chiếu không được cấp phát bộ nhớ, không có địa chỉ riêng.

Chủ đề:
Lưu

Nội dung Text: chương 2

  1. sẽ lưu trữ địa chỉ của biễn x vào con trỏ px. chương 2 Hàm trong C++ 1.2. Biến tham chiếu Trong C++ cho phép sử dụng loại biến thứ ba là biến tham chiếu. Chương này trình b ầy những khả năng mới của C++ trong việc So với 2 loại biến quen biết nói trên, thì biến này có những đặc điểm xây dựng và sử dụng hàm. Đó là: sau: + Kiểu tham chiếu và việc truyền dữ liệu cho hàm bằng tham + Biến tham chiếu không được cấp phát bộ nhớ, không có địa chỉ chiếu. riêng. + Đối tham chiếu hằng (const) + Nó dùng làm bí danh cho một biến (kiểu giá trị) nào đó và nó + Đối có giá trị mặc định sử dụng vùng nhớ của biến này. Ví dụ câu lệnh: + Hàm trực tuyến float u, v, &r = u ; + Việc định nghĩa chồng các hàm tạo ra các biến thực u, v và biến tham chiếu thực r. Biến r không + Việc định nghĩa chồng các toán tử được cấp phát bộ nhớ, nó là một tên khác (bí danh) của u và nó dùng chung vùng nhớ của biến u. Thuật ngữ: Khi r là bí danh (alias) của u thì ta nói r tham chiếu § 1 . Biến tham chiếu (Reference variable) đến biến u. Như vậy 2 thuật ngữ trên được hiểu như nhau. 1.1. Hai loại biến dùng trong C ý nghĩa: Khi r là bí danh của u thì r dùng chung vùng nhớ của u, Trước khi nói đến biến tham chiếu, chúng ta nhắc lại 2 loại biến dó đó : gặp trong C là: + Trong mọi câu lệnh, viết u hay viết r đều có ý nghĩa như nhau, Biến giá trị dùng đ ể chứa dữ liệu (nguyên, thực, ký tự, ... ) vì đều truy nhập đến cùng một vùng nhớ. Biến con trỏ dùng đ ể chứa địa chỉ + Có thể dùng biến tham chiếu để truy nhập đến một biến kiểu giá Các biến này đều đ ược cung cấp bộ nhớ và có địa chỉ. Ví dụ câu trị. lệnh khai báo: Ví dụ: double x , *px; int u, v, &r = u; sẽ tạo ra biến giá trị kiểu double x và biến con trỏ kiểu double px. r = 10 ; // u=10 Biến x có vùng nhớ 8 byte, biến px có vùng nhớ 4 byte (nếu dùng mô hình Large). Biến x d ùng để chứa giá trị kiểu double, ví dụ lệnh cout
  2. &r; // Cho địa chỉ của u Chương trình dưới đây minh hoạ cách d ùng biến tham chiếu đến một phần tử mảng cấu trúc để nhập dữ liệu và thực hiện các phép tính trên các trường của phần tử mảng cấu trúc. Công dụng: Biến tham chiếu thường đ ược sử dụng làm đối của # include hàm để cho phép hàm truy nhập đến các tham số biến trong lời gọi hàm. # include struct TS Vài chú ý về biến tham chiếu: 38 { a. Vì biến tham chiếu không có địa chỉ riêng, nó chỉ là bí danh char ht[25]; của một biến kiểu giá trị nên trong khai báo phải chỉ rõ nó tham chiếu đến biến nào. Ví dụ nếu khai báo: float t,l,h,td; double &x ; }; thì Trình biên dịch sẽ báo lỗi: void main() Reference variable ‘x’ must be initialized { b. Biến tham chiếu có thể tham chiếu đến một phần tử mảng, ví TS ts[10],&h=ts[1]; // h tham chiếu đến ts[1] dụ: cout h.t >> h.l >> h.h ; d. Biến tham chiếu có thể tham chiếu đến một hằng. Khi đó nó sẽ h.td = h.t + h.l + h.h ; sử dụng vùng nhớ của hằng và nó có thể làm thay đổi giá trị chứa cout
  3. Tốn kém về thời gian và bộ nhớ vì phải tạo ra các bản sao. Không int n = 10 ; thao tác trực tiếp trên các tham số, vì vậy không làm thay đ ổi được const int &r = n ; // Hằng tham chiếu r tham chiếu đến biến n giá trị các tham số. const int &s=123 ; //Hằng tham chiếu s tham chiếu đến hằng 123 Sự khác nhau giữa biến và hằng tham chiếu ở chỗ: Không cho 2.2. Truyền giá trị cho hàm theo tham chiếu phép dùng hằng tham chiếu để làm thay đổi giá trị của vùng nhớ mà Trong C++ cung cấp thêm cách truyền dữ liệu cho hàm theo tham nó tham chiếu. chiếu bằng cách dùng đ ối là biến tham chiếu hoặc đối là hằng tham chiếu. Cách này có ưu điểm: Ví dụ: Không cần tạo ra các bản sao của các tham số, do đó tiết kiệm bộ int y = 12, z ; 40 ớ và thời gian chạy máy. nh const int &py=y; // Hằng tham chiếu py tham chiếu đến biến y Hàm sẽ thao tác trực tiếp trên vùng nhớ của các tham số, do đó dễ y++; // Đúng dàng thay đ ổi giá trị các tham số khi cần. z = 2*py ; // Đúng z = 26 cout
  4. void nhapds(double *a, int n) printf("\n%0.1lf",x[i]); { getch(); for (int i=1; i
  5. { cout ts[i].l >> ts[i].h ; cin >> dc; ts[i].td = ts[i].t + ts[i].l + ts[i].h ; cout
  6. { { for (int i=1 ; i
  7. # include return ts; int z ; } int &f() // Hàm trả về một bí danh của biến to àn bộ z void main() { { TS &h=f(); // h tham chiếu đến biến ts return z; } cout h.t >> h.l >> h.h ; cout
  8. void cap_phat_bo_nho_nhapsl(int n) void main() { { int n, i ; ts = new TS[n+1] ; cout > n; { cap_phat_bo_nho_nhapsl(n); cout
  9. Cách 1 (Không khai báo nguyên mẫu): Các ví dụ sai: void delay(int n=1000) d3 và d5 mặc định (khi đó d4 cũng phải mặc định) { d3 và d4 mặc định (khi đó d5 cũng phải mặc định) for (int i=0 ; i
  10. int ymax = getmaxy() ) ; for (int i=0;i
  11. initgraph(&mh,&mode,""); return s*h/2; setbkcolor(BLUE); } hiendc("HELLO"); // HELLO mầu đỏ giữa màn hình void main() hiendc("CHUC MUNG",1,1); // CHUC MUNG mầu đỏ tại vị { // trí (1,1) clrscr(); hiendc("CHAO",1,400,YELLOW); // CHAO mầu vàng tại vị cout
  12. inline int f(int a, int b) 5.2. Các hàm trực tuyến { Để biến một hàm thành trực tuyến ta viết thêm từ khoá return a*b; inline } vào trước khai báo nguyên mẫu hàm. Nếu không dùng nguyên mẫu thì viết từ khoá này trước dòng đầu tiên của định nghĩa hàm. Ví dụ: Chú ý: Trong C++ , nếu hàm được xây dựng sau lời gọi hàm thì bắt buộc phải khai báo nguyên mẫu hàm trước lời gọi. Trong ví dụ inline float f(int n, float x); trên, Trình biên d ịch C++ sẽ bắt lỗi vì thiếu khai báo nguyên mẫu float f(int n, float x) hàm f . { 5.3. Cách biên dịch hàm trực tuyến // Các câu lệnh trong thân hàm Chương trình d ịch xử lý các hàm inline như các macro (được định } nghĩa trong lệnh #define), nghĩa là nó sẽ thay mỗi lời gọi hàm b ằng hoặc một đoạn chương trình thực hiện nhiệm vụ của hàm. Cách này làm inline float f(int n, float x) cho chương trình dài ra, nhưng tốc độ chương trình tăng lên do không phải thực hiện các thao tác có tính thủ tục khi gọi hàm. { // Các câu lệnh trong thân hàm 5.4. So sánh macro và hàm trực tuyến } Dùng macro và hàm trực tuyến đều dẫn đến hiệu quả tương tự, Chú ý: Trong mọi trường hợp, từ khoá inline phải xuất hiện tuy nhiên người ta thích d ùng hàm trực tuyến hơn, vì cách này đảm trước các lời gọi hàm thì Trình biên d ịch mới biết cần xử lý hàm theo bảo tính cấu trúc của chương trình, dễ sử dụng và tránh được các sai kiểu inline. sót lặt vặt thường gặp khi d ùng #define (như thiếu các dấu ngoặc, dấu chấm phẩy) Ví dụ hàm f trong chương trình sau sẽ không phải là hàm trực tuyến vì từ khoá inline viết sau lời gọi hàm: 5.5. Khi nào thì nên dùng hàm trực tuyến # include Phương án dùng hàm trực tuyến rút ngắn đ ược thời gian chạy # include máy nhưng lại làm tăng khối lượng bộ nhớ chương trình (nhất là đ ối void main() với các hàm trực tuyến có nhiều câu lệnh). Vì vậy chỉ nên dùng phương án trực tuyến đối với các hàm nhỏ. { int s ; 58 Sự hạn chế của Trình biên dịch 5.6. s = f(5,6); Không phải khi gặp từ khoá inline là Trình biên d ịch nhất thiết cout
  13. Có một số hàm mà các Trình biên d ịch thường không xử lý theo clrscr(); cách inline như các hàm chứa biến static, hàm chứa các lệnh chu for (i=1;i
  14. Nhờ khả năng định nghĩa chồng, trong C++ có thể d ùng chung } một tên cho cả 3 hàm trên như sau: clrscr(); int abs(int i) ; // Lấy giá trị tuyệt đối giá trị kiểu int for (i=1;i
  15. double, cộng 2 ma trận chữ nhật kiểu int, thì 4 hàm trên nên định void main() nghĩa chồng (đặt cùng tên). { + Nên dùng các phép chuyển kiểu (nếu cần) để bộ tham số trong int b=f(5); lời gọi ho àn toàn trùng kiểu với bộ đối số của một hàm được định f(b); nghĩa chồng. Vì như thế mới tránh đ ược sự nhập nhằng cho Trình getch(); biên dịch và Trình biên d ịch sẽ chọn đúng hàm cần gọi. } 6.5. Lấy địa chỉ các hàm trùng tên 6.3. Sử dụng các hàm định nghĩa chồng Giả sử có 4 hàm đều có tên là tinh_max được khai báo như sau: Khi gặp một lời gọi, Trình biên dịch sẽ căn cứ vào số lượng và int tinh_max(int a, int b, int c) ; // Max của 3 số nguyên kiểu của các tham số để gọi hàm có đúng tên và đúng bộ đối số double tinh_max(double a, double b, double c); // Max của 3 tương ứ ng. Ví dụ: số // thực abs(123); // Tham số kiểu int, gọi hàm int abs(int i) ; int tinh_max(int *a, int n) ; // Max của một dẫy số nguyên abs(123L); // Tham số kiểu long, gọi hàm long abs(long l); double tinh_max(double *a, int n) ; //Max của một dẫy số thực abs(3.14); //Tham số kiểu double, gọi hàm double abs(double d); Vấn đề đặt ra là làm thế nào lấy đ ược địa chỉ của mỗi hàm. Câu trả lời như sau: Khi không có hàm nào có bộ đối cùng kiểu với bộ tham số (trong lời gọi), thì Trình biên d ịch sẽ chọn hàm nào có bộ đối gần kiểu nhất Để lấy địa chỉ của một hàm, ta khai báo một con trỏ hàm có kiểu (phép chuyển kiểu dễ dàng nhất). Ví dụ: và b ộ đối như hàm cần lấy địa chỉ. Sau đó gán tên hàm cho con trỏ hàm. Ví d ụ: abs(‘A’) ; // Tham số kiểu char, gọi hàm int abs(int i) ; int (*f1)(int , int, int ); abs(3.14F); // Tham số kiểu float, gọi hàm double abs(double d); // Lấy địa chỉ của hàm thứ nhất f1 = tinh_max ; 6.4. Nên sử dụng phép định nghĩa chồng các hàm như thế nào double (*f2)(double , double, double); Như đ ã nói ở trên, khi xây d ựng cũng như sử dụng các hàm trùng // Lấy địa chỉ của hàm thứ hai f2 = tinh_max ; tên, Trình biên d ịch C++ đ ã phải suy đoán và giải quyết nhiều trường int (*f3)(int *, int ); hợp khá nhập nhằng. Vì vậy không nên lạm dụng quá đáng khả năng // Lấy địa chỉ của hàm thứ ba f3 = tinh_max ; định nghĩa chồng, vì đ iều đó làm cho chương trình khó kiểm soát và dễ dẫn đến sai sót. Việc định nghĩa chồng sẽ hiệu quả hơn nếu được double (*f4)(double *, int ); sử dụng theo các lời khuyên sau: // Lấy địa chỉ của hàm thứ tư f4 = tinh_max ; + Chỉ nên đ ịnh nghĩa chồng các hàm thực hiện những công việc 6.6. Các ví dụ như nhau nhưng trên các đối tượng có kiểu khác nhau. Ví dụ trong chương trình cần xây dựng các hàm: cộng 2 ma trận vuông kiểu Ví dụ 1: Chương trình giải bài toán tìm max của một dẫy số double, cộng 2 ma trận vuông kiểu int, cộng 2 ma trân chữ nhật kiểu nguyên và max của một dẫy số thực. Trong chươmg trình có 6 hàm. Hai hàm dùng để nhập dẫy số nguyên và dẫy số thực có tên chung là 64
  16. nhapds. Bốn hàm: tính max 2 số nguyên, tính max 2 số thực, tính } max của dẫy số nguyên, tính max của dẫy số thực được đặt chung double max(double x, double y) một tên là max. { # include return x>y?x:y ; # include } # include int max(int *x, int n) void nhapds(int *x, int n); { void nhapds(double *x, int n); int max(int x, int y); int s=x[1]; double max(double x, double y); for (int i=2;i
  17. cout > nd ; for (int i=1;i
  18. for (int j=1;j
  19. PS chia(PS p1, PS p2); return q; void nhap(PS *p) } { PS cong(PS p1, PS p2) int t, m; { printf("\nTu va mau: "); PS q; scanf("%d%d", &t, &m); q.a = p1.a*p2.b + p2.a*p1.b; 70 71 p->a = t; p->b = m; q.b = p1.b * p2.b ; } return rutgon(q); void in(PS p) } { PS tru(PS p1, PS p2) printf(" %d/%d",p.a,p.b); { } PS q; int uscln(int x, int y) q.a = p1.a*p2.b - p2.a*p1.b; { q.b = p1.b * p2.b ; x=abs(x); y=abs(y); return rutgon(q); if (x*y==0) return 1; } while (x!=y) PS nhan(PS p1, PS p2) if (x>y) x-=y; { else y-=x; PS q; return x; q.a = p1.a * p2.a ; } q.b = p1.b * p2.b ; PS rutgon(PS p) return rutgon(q); { } PS q; PS chia(PS p1, PS p2) int x; { x=uscln(p.a,p.b); PS q; q.a = p.a / x ; q.a = p1.a * p2.b ; q.b = p.b / x ; q.b = p1.b * p2.a ;
  20. toán để định nghĩa các hàm, mà ta thường gọi là định nghĩa chồng return rutgon(q); các toán tử (hay còn gọi: Sự tải bội các toán tử). } 7.3. Cách định nghĩa chồng các toán tử void main() 7.3.1.Tên hàm toán tử: Gồm từ khoá operator và tên phép toán, { ví dụ: PS p, q, z, u, v ; operator+ (định nghĩa chồng phép +) PS tu,mau, s; (định nghĩa chồng phép -) operator- printf("\n Nhap phan so p: "); nhap(&p); 72 73 7.3.2. Các đối của hàm toán tử: printf("\n Nhap phan so q: ");nhap(&q); a. Với các phép toán có 2 toán hạng, thì hàm toán tử cần có 2 printf("\n Nhap phan so z: ");nhap(&z); đối. Đối thứ nhất ứng với toán hạng thứ nhất, đối thứ hai ứng với printf("\n Nhap phan so u: ");nhap(&u); toán hạng thứ hai. Do vậy, với các phép toán không giao hoán (như phép-) thì thứ tự đối là rất quan trọng. printf("\n Nhap phan so v: ");nhap(&v); Ví dụ các hàm toán tử cộng , trừ phân số đ ược khai báo như sau: tu = nhan(q,z); struct PS tu = tru(p,tu) ; { mau = cong(u,v) ; int a; // Tử số s = chia(tu,mau); int b; // Mẫu số printf(“\n Phan so s = “); in(s); }; getch(); PS operator+(PS p1, PS p2); // p1 + p2 } PS operator-(PS p1, PS p2); // p1 - p2 PS operator*(PS p1, PS p2); // p1 * p2 Nhận xét: Việc sử dụng các hàm đ ể thực hiện các phép tính không được tự nhiên và tỏ ra dài dòng. Ví dụ để thực hiện một công PS operator/(PS p1, PS p2); // p1 / p2 thức b. Với các phép toán có một toán hạng, thì hàm toán tử có một s = (p - q*z)/(u + v) đối. Ví dụ hàm toán tử đổi dấu ma trận (đổi dấu tất cả các phần tử phải dùng 2 biến trung gian và 4 lời gọi hàm. Câu hỏi đặt ra là có của ma trận) đ ược khai báo như sau: cách nào để chỉ cần viết đúng công thức toán học, mà vẫn nhận được struct MT kết quả mong muốn hay không? { Trong C++ có thể đáp ứng được mong muốn này b ằng cách sử double a[20][20] ; // Mảng chứa các phần tử ma trận dụng các phép toán chuẩn của nó cho các kiểu dữ liệu tự định nghĩa int m ; // Số hàng ma trận (mảng, cấu trúc, ...). Nói cách khác C++ cho phép dùng các phép
ADSENSE
ADSENSE

CÓ THỂ BẠN MUỐN DOWNLOAD

 

Đồng bộ tài khoản