1. Trang chủ
  2. » Công Nghệ Thông Tin

Quá tải hàm

32 631 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Quá Tải Hàm
Định dạng
Số trang 32
Dung lượng 54,85 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Bài giảng lập trình hướng đối tượng - Thầy Cường Học viện bưu chính viễn thông TP HCM

Trang 1

Chương 4

Quá tải hàm

• Quá tải hàm tạo

• Hàm tạo bản sao

• Hàm với các đối số mặc định

• Tính không xác định khi quá tải hàm

• Điạ chỉ của hàm quá tải

Trang 3

I/ Quá tải hàm tạo (constructor overloading )

Có thể quá tải hàm tạo của một lớp, nhưng không quá tải hàm hủy

Hàm tạo của lớp phải phù hợp với cách mà đối tượng của lớp đó được khai báo Nếu không lỗi thời gian biên dịch sẽ xảy ra

Có 3 lý do cần quá tải hàm tạo :

+ để có tính linh hoạt

+ để hổ trợ mảng

+ để tạo các hàm tạo bản sao

Hạn chế : nếu thực hiện quá tải nhiều lần có thể tạo ra tác dụng hủy hoại trên lớp

Quá tải hàm tạo với khởi đầu một đối tượng hoặc không khởi đầu đối tượng

myclass o1(10); // declare with initial value

myclass o2; // declare without initializer

cout << "o1: " << o1.getx() << '\n';

cout << "o2: " << o2.getx() << '\n';

return 0;

}

Trang 4

• Quá tải hàm tạo để cho các đối tượng riêng lẽ lẫn các mảng đối tượng xảy ra trong chương trình

myclass o2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // declare with initializers

int i;

for(i=0; i<10; i++) {

cout << "o1[" << i << "]: " << o1[i].getx();

Trang 6

• Quá tải hàm tạo khi mảng động của lớp được cấp phát Điều này giúp giải quyết hạn chế trong chương 3, phần IV/2, một mảng động không thể được khởi đầu, xem ví dụ 4.6

myclass ob(10); // initialize single variable

p = new myclass[10]; // can't use initializers here

// initialize all elements to ob

for(i=0; i<10; i++) p[i] = ob;

for(i=0; i<10; i++) {

cout << "p[" << i << "]: " << p[i].getx();

cout << '\n';

}

Trang 7

Thực hiện việc kiểm tra giới hạn biên cần thiết và chứng tỏ các hàm tạo sẽ hoạt động qua một chương trình ngắn

class strtype {

char *p;

int len;

public:

char *getstring() { return p; }

int getlength() { return len; }

Hàm tạo bản sao là kiểu đặc biệt của hàm tạo được quá tải

Khi một đối tượng được truyền cho một hàm, bản sao từng bit một của đối tượng đó được tạo ra và được truyền cho tham số của hàm để nhận đối tượng

Trang 8

Tuy nhiên, có những trường hợp trong đó bản sao đồng nhất là như không mong muốn

Chẳng hạn, nếu đối tượng có con trỏ tới bộ nhớ được cấp phát, thì bản sao sẽ trỏ tới

cùng bộ nhớ như đối tượng gốc đã làm Do đó, nếu bản sao tạo ra sự thay đổi cho nội dung bộ nhớ thì nó cũng sẽ được thay đổi đối với đối tượng gốc Khi một hàm kết thúc, bản sao sẽ bị hủy và hàm hủy của nó được gọi Điều này dẫn đến những tác dụng không mong muốn làm ảnh hưởng đến đối tượng gốc

Khi một đối tượng được trả về từ một hàm tình trạng tương tự cũng sẽ xảy ra Trình

biên dịch sẽ tạo ra một đối tượng tạm để giữ bản sao của giá trị do hàm trả về Đối

tượng tạm này sẽ ra khỏi phạm vi một khi giá trị được trả về cho thủ tục gọi, khiến hàm hủy của đối tượng tạm được gọi Nếu hàm hủy hủy bỏ thứ gì cần cho thủ tục gọi, chẳng hạn nó giải phóng bộ nhớ cấp phát động, thì rắc rối sẽ xảy ra

Như vậy, cốt lõi của vấn đề trên là bản sao từng bit của đối tượng được tạo ra và

thực hiện Để ngăn chặn vấn đề này, lập trình viên cần xác định chính xác những gì xảy ra khi bản sao của một đối tượng được thực hiện để tránh được những tác dụng không mong muốn

Hàm tạo bản sao sẽ giải quyết được vấn đề trên Khi định nghĩa hàm tạo bản sao, lập trình viên có thể hoàn toàn kiểm soát chính xác những gì xảy ra khi bản sao của một đối tượng được thực hiện

• Cần phân biệt hai trường hợp trong đó giá trị của một đối tượng được truyền cho đối tượng khác :

+ Trường hợp thứ nhất là phép gán

+ Trường hợp thứ hai là sự khởi đầu, có thể xảy ra theo 3 cách :

- Khi một đối tượng được dùng để khởi đầu một đối tượng khác trong

câu lệnh khai báo

- Khi một đối tượng được truyền như tham số cho hàm

- Khi một đối tượng tạm được tạo ra dùng để làm giá trị trả về bởi một hàm

Hàm tạo bản sao chỉ áp dụng cho sự khởi đầu Nó không áp dụng cho phép gán

Hàm tạo bản sao không ảnh hưởng đến các phép gán

Trang 9

2/ Cú pháp

• Dạng tổng quát của hàm tạo bản sao

classname(const classname &obj) {

// body of constructor

}

obj là một tham chiếu tới một đối tượng được dùng để khởi đầu một đối tượng khác

• Dạng mở rộng của hàm tạo bản sao (có nhiều đối số)

classname(const classname &obj, int x = 0) {

// body of constructor }

Đối số thứ nhất là một tham chiếu tới đối tượng được sao chép, và các đối tượng khác đều mặc định Tính linh hoạt này cho phép tạo ra các hàm tạo bản sao có những công dụng khác

Ví dụ, lớp được gọi là myclass và y là đối tượng của myclass thì các lệnh sau đây sẽ dùng đến hàm tạo bản sao của myclass :

myclass x = y; // y explicitly initializing x

func1(y); // y passed as a parameter

y = func2(); // y receiving a returned object

Trong hai trường hợp đầu, một tham chiếu tới y sẽ được truyền cho hàm tạo bản sao Trường hợp thứ ba, một tham chiếu tới đối tượng được trả về bởi func2() sẽ được truyền cho hàm tạo bản sao

• Tạo một mảng số nguyên "an toàn" có kiểm tra giới hạn biên

Ví dụ 2.1

Trang 10

/* This program creates a "safe" array class Since space for the array is dynamically allocated, a copy constructor is provided to allocate memory when one array object is used to initialize another */

array(const array &a);

void put(int i, int j) {

if(i>=0 && i<size) p[i] = j;

array::array(const array &a) {

Trang 11

int i;

size = a.size;

p = new int[a.size]; // allocate memory for copy

if(!p) exit(1);

for(i=0; i<a.size; i++) p[i] = a.p[i]; // copy contents

cout << "Using copy constructor\n";

// put some values into the array

for(i=0; i<10; i++) num.put(i, i);

// display num

for(i=9; i>=0; i ) cout << num.get(i);

cout << "\n";

// create another array and initialize with num

array x = num; // this invokes copy constructor

// display x

for(i=0; i<10; i++) cout << x.get(i);

return 0;

}

@ Có nhận xét gì về kết quả khi chương trình này không sử dụng hàm tạo bản sao ?

@ Những câu lệnh sau không thể gọi được hàm tạo bản sao, tại sao ?

array a(10);

array b(10);

b = a; // does not call copy constructor

Trang 12

• Hàm tạo bản sao giúp ngăn ngừa một số vấn đề liên quan đến việc truyền các kiểu đối tượng nào đó cho hàm

Trang 13

@ Hãy tìm nguyên nhân gây ra lỗi ?

Ví dụ 2.3 Hiệu chỉnh chương trình trong ví dụ 2.2

/* This program uses a copy constructor to allow strtype objects to be passed to functions */

strtype(const strtype &o); // copy constructor

~strtype() { delete [] p; } // destructor

char *get() { return p; }

Trang 16

2 Tìm lỗi sai trong chương trình và yêu cầu chỉnh lý ?

cout << getval(a) << " " << getval(b) << "\n";

cout << getval(a) << " " << getval(b);

return 0;

}

Trang 17

III/ Sử dụng các đối số mặc định (default argument)

Đối số mặc định cho phép gán một giá trị mặc định cho một tham số khi không có đối số tương ứng được chỉ rõ khi hàm được gọi

Đối số mặc định liên quan đến sự quá tải hàm, sử dụng các đối số mặc định chủ

yếu là dạng ngắn của quá tải hàm

Để gán một đối số mặc định cho một tham số thì sau tham số phải có dấu bằng

và một giá trị muốn mặc định nếu không có đối số tương ứng khi hàm được gọi

Hàm f() cho hai giá trị mặc định 0 đối với các tham số

void f(int a = 0, int b = 0);

Cú pháp này tương tự như khởi đầu một biến

Trang 18

Hàm f() có thể gọi theo 3 cách :

+ nó được gọi vơí cả hai đối số được chỉ rõ

+ nó được gọi chỉ vơí đối số thứ nhất được chỉ rõ Trường hợp này, b sẽ mặc định về

0

+ f() có thể được gọi là không có đối số , a và b mặc định về 0

nghiã là các câu lệnh sau đều đúng :

f() ; // a and b default to 0

f(10) ; // a is 10, b defaults to 0

f(10, 99) ; // a is 10, b is 99

Lưu ý, trong ví dụ này, không có cách để mặc định a và chỉ rõ b

• Khi đối số mặc định đầu tiên được chỉ rõ, tất cả các tham số theo sau cũng phải có mặc định Ví dụ một phiên bản khác của f() sau đây sẽ tạo ra lỗi thời gian biên dịch :

void f(int a = 0, int b) // wrong! b must have default, too

Khi tạo một hàm có một hay nhiều đối số mặc định thì những đối số đó phải

chỉ rõ một lần hoặc trong định nghiã của hàm hoặc trong nguyên mẫu của nó chứ không được cả hai

Qui tắc này áp dụng ngay cả khi chỉ nhân đôi các ngầm định giống nhau

Tất cả các tham số mặc định phải ở bên phải của các tham số không có mặc

định Hơn nữa, khi bắt đầu định nghĩa các tham số mặc định, không thể chỉ định các tham số không có mặc định

• Với các đối số mặc định, chúng phải là các hằng hoặc là các biến toàn cục

Trang 19

Ví dụ 3.2 Các đối số mặc định liên quan đến sự quá tải hàm

// Compute area of a rectangle using overloaded functions

#include <iostream.h>

// Return area of a non-square rectangle

double rect_area(double length, double width)

{

return length * width;

}

// Return area of a square

double rect_area(double length)

// Compute area of a rectangle using default arguments

#include <iostream.h>

// Return area of a rectangle

Trang 20

double rect_area(double length, double width = 0)

{

if(!width) width = length;

return length * width;

@ Như vậy, các đối số ngầm định thường đưa ra cách đơn giản để quá tải hàm

• Trong phần trước, một hàm tạo được quá tải chiû cho phép tạo ra các đối tượng

được khởi đầu vẫn không được khởi đầu Trong nhiều trường hợp, có thể tránh

quá tải một hàm tạo bằng cách cho nó một hay nhiều đối số ngầm định

myclass o1(10); // declare with initial value

myclass o2; // declare without initializer

Trang 21

cout << "o1: " << o1.getx() << '\n';

cout << "o2: " << o2.getx() << '\n';

return 0;

}

@ Như vậy, bằng cách cho biến n giá trị mặc định zero, có thể tạo ra các đối tượng

có các giá trị đầu rõ ràng và các đối tượng có giá trị mặc định là đủ

• Đối số mặc định được tìm thấy khi một tham số được dùng để chọn một tùy chọn

Ví dụ 3.5

#include <iostream.h>

#include <ctype.h>

const int ignore = 0;

const int upper = 1;

const int lower = 2;

void print(char *s, int how = -1) ;

int main()

{

print("Hello There\n", ignore);

print("Hello There\n", upper);

print("Hello There\n"); // continue in upper

print("Hello there\n", lower);

print("That's all\n"); // continue in lower

Trang 22

// reuse old case if none specified

if(how < 0) how = oldcase;

Trang 23

Bài tập III

1 Tìm lỗi sai trong nguyên mẫu hàm sau :

char *f(char *p, int x = 0 , char *q) ;

2 Tìm lỗi sai trong nguyên mẫu dùng đối số mặc định sau :

int f(int count, int max = count) ;

3 Trong thư viện chuẩn C++ có hàm strtol(), có nguyên mẫu sau :

long strtol(const char *start, const **end, int base);

Hàm này chuyển đổi chuỗi số được trỏ tới bởi start thành số nguyên dài

Hãy tạo hàm mystrtol() hoạt động giống như strtol() với ngoại lệ là base được cho một đối số mặc định là 10 Viết chương trình chứng tỏ hàm mystrtol() hoạt động tốt

4 Hãy tạo hàm myclreol() để xoá dòng từ vị trí con trỏ hiện tại đến cuối dòng Hàm này có một tham số chỉ rõ vị trí của ký tự cần xoá Nếu không chỉ rõ tham số thì toàn bộ dòng sẽ tự động bị xoá Ngược lại, chỉ xoá những vị trí nào được chỉ rõ bởi tham số

IV/ Sự quá tải và tính không xác định (ambiguity)

Trang 24

Khi quá tải hàm, có thể dẫn đến tính không xác định trong chương trình

Tính không xác định do quá tải gây ra có thể được đưa vào thông qua :

+ các chuyển đổi kiểu

+ các tham số tham chiếu

+ các đối số mặc định

+ hoặc do chính các hàm được quá tải

+ hoặc do cách gọi các hàm quá tải

Tính không xác định phải được loại bỏ trước khi chương trình được biên dịch

• Tính không xác định gây ra bởi các quy tắc chuyển đổi kiểu tự động trong C++

cout << f(x); // unambiguous - use f(float)

cout << f(y); // unambiguous - use f(double)

Trang 25

cout << f(10); // ambiguous, convert 10 to double or float??

return 0;

}

@ Lỗi biên dịch có dạng Error : Ambiguity between 'f(float)' and 'f(double)'

@ Ví dụ trên minh họa tính không xác định có thể xảy ra khi một hàm quá tải được gọi

Khi hàm được gọi với kiểu đối số sai, các quy tắc chuyển đổi tự động của C++

gây ra tình trạng không xác định Bản thân sự quá tải hàm ở ví dụ này tự nó không có tính không xác định

Trang 26

• Tính không xác định xảy ra do quá tải các hàm, trong đó có sự khác biệt duy nhất là một hàm sử dụng một tham số tham chiếu và hàm kia sử dụng tham số mặc định gọi bằng giá trị

// this is inherently ambiguous

int f(int a, int &b)

@ Lỗi biên dịch ở hàm f(int a, int &b) có dạng

Error : 'f(int, int &)' cannot be distinguished from 'f(int, int)'

Trang 27

• Tính không xác định xảy ra do quá tải hàm, trong đó có một hay nhiều hàm được quá tải dùng một đối số mặc định

cout << f(10, 2); // calls f(int, int)

cout << f(10); // ambiguous - call f(int) or f(int, int)???

return 0;

}

Trang 28

V/ Tìm điạ chỉ của một hàm quá tải

Trong ngôn ngữ C, để biết điạ chỉ một hàm, dùng con trỏ p trỏ đến hàm đó, chẳng hạn

p = zap; // với hàm zap()

Trong ngôn ngữ C++, vấn đề hơi phức tạp hơn bởi vì hàm có thể được quá tải

Cơ chế : dùng cách khai báo con trỏ xác định điạ chỉ của hàm quá tải nào sẽ thu

// Output count number of spaces

void space(int count)

{

for( ; count; count ) cout << ' ';

}

// Output count number of chs

void space(int count, char ch)

Trang 29

// and one character parameter

void (*fp2)(int, char);

fp1 = space; // gets address of space(int)

fp2 = space; // gets address of space(int, char)

1 Cho hai hàm quá tải Tìm điạ chỉ của mỗi hàm

int dif(int a, int b)

Trang 30

Bài tập chương 4

1 Hãy quá tải hàm tạo date() trong ví dụ 1.3, chương 4 để cho nó nhận một tham số kiểu time_t

2 Tìm lỗi sai trong chương trình sau :

4 Tìm lỗi sai trong đoạn chương trình sau :

void compute(double *num, int divisor=1);

void compute(double *num);

//

compute(&x);

5 Hãy tạo hàm order() để nhận hai tham số tham chiếu nguyên Nếu đối số thứ nhất lớn hơn đối số thứ hai, hãy đảo ngược hai đối số Ngược lại, không tác động nào Ví dụ

Trang 31

int x=1, y=0;

order(x, y);

6 Tại sao hai hàm quá tải sau vốn không xác định ?

int f(int a);

int f(int &a);

7 Cho lớp sau, hãy bổ sung các hàm tạo cần thiết để cho cả hai khai báo trong main() đều đúng

samp ob(88); // init ob's a to 88

samp obarray[10]; // noninitialized 10-element array

Trang 32

10 Cho lớp sau đây, có thể cấp phát động một mảng các đối tượng này không ?

Ngày đăng: 21/08/2012, 15:34

Xem thêm

w