Tải bản đầy đủ (.pdf) (32 trang)

Quá tải hàm

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (54.85 KB, 32 trang )





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













Chöông 4
Quaù taûi haøm


104
104









































Chương 4
Quá tải hàm



105
105
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

Ví dụ 1.1
#include <iostream.h>

class myclass {

int x;
public:
// overload constructor two ways

myclass() { x = 0; }
// no initializer

myclass(int n) { x = n; }
// initializer
int getx() { return x; }
};

int main()
{
myclass
o1(10)
; // declare with initial value
myclass
o2
; // declare without initializer

cout << "o1: " << o1.getx() << '\n';
cout << "o2: " << o2.getx() << '\n';
return 0;
}
Chương 4
Quá tải hàm


106

106

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.

Ví dụ 1.2
#include <iostream.h>

class myclass {
int x;
public:
// overload constructor two ways

myclass() { x = 0; }
// no initializer

myclass(int n) { x = n; }
// initializer

int getx() { return x; }
};

int main()
{
myclass
o1[10]
; // declare array without initializers
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();
cout << '\n';
cout << "o2[" << i << "]: " << o2[i].getx();
cout << '\n';
}

return 0;
}



Quá tải hàm tạo giúp lập trình viên chọn phương pháp thuận lợi nhất để khởi
đầu một đối tượng.

Chửụng 4
Quaự taỷi haứm


107
107
Vớ duù 1.3
#include <iostream.h>
#include <stdio.h> // included for sscanf()

class date {
int day, month, year;

public:

date(char *str)
;

date (int m, int d, int y)
{
day = d;
month = m;
year = y;
}

void show() {
cout << month << '/' << day << '/';
cout << year << '\n';
}
};

date::date(char *str)
{
sscanf(str, "%d%*c%d%*c%d", &month, &day, &year);
}

int main()
{
// construct date object using string

date sdate("12/31/99")
;


// construct date object using integers

date idate(12, 31, 99)
;

sdate.show();
idate.show();
return 0;
}
Chương 4
Quá tải hàm


108
108

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.

Ví dụ 1.4
#include <iostream.h>

class myclass {
int x;
public:
// overload constructor two ways

myclass() { x = 0; }
// no initializer


myclass(int n) { x = n; }
// initializer
int getx() { return x; }
void setx(int n) { x = n; }
};

int main()
{
myclass *p;
myclass ob(10); // initialize single variable

p = new myclass[10]; // can't use initializers here
if(!p) {
cout << "Allocation error\n";
return 1;
}

int i;

// 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';
}
Chương 4

Quá tải hàm


109
109

return 0;
}


Bài tập I

1. Cho lớp sau, hãy bổ sung hai hàm tạo. Hàm tạo thứ nhất không nhận tham số,
hàm này dùng toán tử new cấp phát 255 bytes bộ nhớ, khởi đầu bộ nhớ như một
chuỗi rỗng và cho len = 255. Hàm tạo thứ hai nhận hai tham số, tham số thứ nhất là
chuỗi dùng để khởi đầu và tham số kia là số byte để cấp phát. Cho phiên bản này
cấp phát lượng bộ nhớ đã được chỉ rõ và chép chuổi vào bộ nhớ.

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; }
};



2. Từ bài tập II, 2 chương 2, bổ sung hàm tạo không nhận tham số và một phiên bản
được quá tải để nhận giờ hệ thống dưới dạng được trả về bởi hàm chuẩn clock().


II/ Tạo và sử dụng hàm tạo bản sao
(copy constructor)


1/ Khái niệm
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.
Chương 4
Quá tải hàm


110
110
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.
Chương 4
Quá tải hàm


111
111
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
Chöông 4
Quaù taûi haøm


112
112
/* 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. */

#include <iostream.h>
#include <stdlib.h>

class array {
int *p;
int size;
public:
array(int sz) { // constructor

p = new int[sz];
if(!p) exit(1);
size = sz;
cout << "Using 'normal' constructor\n";
}
~array() {delete [] p;}

// copy constructor

array(const array &a)
;

void put(int i, int j) {
if(i>=0 && i<size) p[i] = j;
}

int get(int i) {
return p[i];
}
};


/* Copy constructor.
In the following, memory is allocated specifically for the copy, and the address of
this memory is assigned to p. Therefore, p is not pointing to the same dynamically
allocated memory as the original object. */
array::array(const array &a)
{
Chương 4
Quá tải hàm



113
113
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";
}

int main()
{
array num(10); // this calls "normal" constructor
int i;

// 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
Chương 4
Quá tải hàm


114
114

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.


Ví dụ 2.2
// This program has an error.
#include <iostream.h>
#include <string.h>
#include <stdlib.h>

class strtype {

char *p;
public:
strtype(char *s);
~strtype() { delete [] p; }
char *get() { return p; }
};

strtype::strtype(char *s)
{
int l;
l = strlen(s)+1;

p = new char [l];
if(!p) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, s);
}

void show(strtype x)
{
char *s;
s = x.get();
cout << s << "\n";
}

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×