1/ Việc truyền các đối tượng cho hàm giống như truyền các đối số thông thường.
Tham số của hàm có kiểu dữ liệu là kiểu lớp, và đối số truyền cho hàm chính là đối tượng.
Giống như các kiểu dữ liệu khác, theo ngầm định, tất cả các đối tượng được truyền bởi giá trị cho một hàm.
Ví dụ 6.1 Truyền một đối tượng cho hàm. #include <iostream.h>
class samp { int i;
samp(int n) { i = n; } int get_i() { return i; } };
// Return square of o.i.
int sqr_it(samp o)
{
return o.get_i() * o.get_i(); } int main() { samp a(10), b(2); cout << sqr_it(a) << "\n"; cout << sqr_it(b) << "\n"; return 0; }
2/ Do đối tượng được truyền bởi giá trị cho một hàm, có nghĩa là việc sao chép từng bit của đối số được thực hiện và chính sao chép này được sử dụng bởi hàm. Do đó, những thay đổi cho đối tượng bên trong hàm không có ảnh hưởng đến sự gọi đối tượng. Ví dụ 6.2 #include <iostream.h> class samp { int i; public: samp(int n) { i = n; } void set_i(int n) { i = n; } int get_i() { return i; } };
however.
void sqr_it(samp o)
{
o.set_i( o.get_i() * o.get_i() ) ;
cout << "Copy of a has i value of " << o.get_i(); cout << "\n";
}
int main() {
samp a(10);
sqr_it(a) ; // a passed by value, displays 100 cout << "But, a.i is unchanged in main: ";
cout << a.get_i(); // displays 10 return 0;
}
3/ Khi truyền điạ chỉ của đối tượng sang hàm (sẽ không tạo ra bản sao đối tượng), nội dung của đối tượng sẽ bị thay đổi.
Ví dụ 6.3 #include <iostream.h> class samp { int i; public: samp(int n) { i = n; } void set_i(int n) { i = n; } int get_i() { return i; } };
/* Set o.i to its square. This affects the calling argument. */
void sqr_it(samp *o)
o->set_i(o->get_i() * o->get_i());
cout << "Copy of a has i value of " << o->get_i(); cout << "\n";
}
int main() {
samp a(10);
sqr_it(&a); // pass a's address to sqr_it(), displays 100 cout << "Now, a in main() has been changed: ";
cout << a.get_i(); // displays 100 return 0;
}
4/ Bản sao của đối tượng
Khi truyền một đối tượng cho hàm, một bản sao của đối tượng được thực hiện, có nghĩa là một đối tượng mới xuất hiện. Do đó khi hàm kết thúc làm việc, bản sao của đối tượng đó (đối số của hàm) sẽ bị hủy.
Điều này làm nảy sinh hai vấn đề :
- Hàm tạo của đối tượng được gọi khi bản sao thực hiện ? - Hàm hủy của đối tượng được gọi khi bản sao bị hủy ?
Khi bản sao của một đối tượng được thực hiện để dùng gọi hàm, thì hàm tạo không được gọi. Do hàm tạo thường được dùng để khởi đầu một khiá cạnh nào đó của đối tượng. Khi truyền một đối tượng cho một hàm, bạn cần đến trạng thái hiện hành của đối tượng chứ không phải trạng thái ban đầu.
Khi hàm kết thúc và bản sao bị hủy, hàm hủy được gọi.
Ví dụ 6.4
#include <iostream.h> class samp {
public:
samp(int n) { i = n;
cout << "Constructing\n"; }
~samp() { cout << "Destructing\n"; } int get_i() { return i; }
};
// Return square of o.i.
int sqr_it(samp o)
{
return o.get_i() * o.get_i(); } int main() { samp a(10); cout << sqr_it(a) << "\n"; return 0; }
Nội dung chương trình thực hiện Constructing
Destructing 100
Destructing Giải thích ?
Hàm hủy của bản sao đối tượng được gọi khi hàm kết thúc có thể là nguyên nhân của nhiều vấn đề.
Chẳng hạn, nếu đối tượng có hàm tạo cấp phát bộ nhớ động hoặc hàm hủy giải phóng bộ nhớ động, thì bản sao của đối tượng sẽ giải phóng bộ nhớ động khi hàm
hủy của nó được gọi. Điều này làm cho đối tượng gốc bị hỏng và trở thành vô dụng.
Ví dụ 6.5
// This program contains an error. #include <iostream.h> #include <stdlib.h> class dyna { int *p; public: dyna(int i);
~dyna() { free(p); cout << "freeing \n"; } int get() { return *p; }
};
dyna::dyna(int i) {
p = (int *) malloc(sizeof(int)); if(!p) {
cout << "Allocation failure\n"; exit(1);
} *p = i; }
// Return negative value of *ob.p
int neg(dyna ob)
{ return -ob.get(); } int main() { dyna o(-10); cout << o.get() << "\n"; // -10
cout << neg(o) << "\n"; // freeing
// 10 dyna o2(20);
cout << o2.get() << "\n"; // 20
cout << neg(o2) << "\n"; // freeing
// -20
cout << o.get() << "\n"; // 20 do *p được cấp địa chỉ vùng nhớ động
cout << neg(o) << "\n"; // freeing
// -20 return 0; // freeing o
// freeing o2
// Null pointer assignment
}
Giải thích nguyên nhân gây lổi ?
• Có thể khắc phục bằng cách truyền điạ chỉ của đối tượng cho hàm. Vì sẽ không có đối tượng mới được tạo ra và không có hàm hủy của bản sao đối tượng được gọi khi hàm trả về.
Tuy nhiên, giải pháp tốt nhất là sử dụng hàm tạo bản sao (copy constructor), cho
phép định nghiã cách thức tạo các bản sao của các đối tượng (xem chương sau).
Bài tập VI
1. Viết chương trình tạo lớp stack trong ví dụ 5.3 chương 2, bổ sung hàm showstack() để truyền một đối tượng kiểu stack. Cho hàm này hiển thị nội dung của ngăn xếp.
2. Tìm lỗi sai trong chương trình này #include <iostream.h>
#include <stdlib.h> class dyna {
int *p; public:
dyna(int i);
~dyna() { free(p); cout << "freeing \n"; } int get() { return *p; }
};
dyna::dyna(int i) {
p = (int *) malloc(sizeof(int)); if(!p) {
cout << "Allocation failure\n"; exit(1);
} *p = i; }
// Return negative value of *ob.p int neg(dyna ob)
{ return -ob.get(); } int main() { dyna o(-10); cout << o.get() << "\n"; cout << neg(o) << "\n"; dyna o2(20); cout << o2.get() << "\n"; cout << neg(o2) << "\n"; cout << o.get() << "\n"; cout << neg(o) << "\n"; return 0;
}