Toán tử đa năng
BÀI 4
Lập trình Hướng đối
1
Mục đích bài học
Toán tử đa năng :
Toán
Toán
Toán
Toán
Toán
tử
tử
tử
tử
tử
một ngôi
hai ngôi
số học hai ngôi
gán phức
so sánh
Đa năng với toán tử gán
Copy constructors
Lập trình Hướng đối
2
Mục đích bài học
Hàm chuyển đổi kiểu
Từ kiểu cơ bản thành kiểu do người
dùng định nghĩa và ngược lại.
Giữa các đối tượng thuộc các lớp
khác nhau.
Những toán tử không thể được
định nghĩa lại.
Lập trình Hướng đối
3
Định nghĩa lại toán tử
Ta có thể kết hợp một toán tử với một hàm thành
viên của lớp. Sau đó toán tử này có thể được dùng
để thực hiện các phép toán trên các đối tượng của
lớp.
Các toán tử +, -, *, / chỉ có thể được dùng trong các
biểu thức với các toán hạng có kiểu cơ bản như int,
float.
Định nghĩa lại một toán tử cho phép ta thực hiện
các phép toán như :
if (obj1 > obj2) { ... }
với obj1 và obj2 là các đối tượng của một lớp.
Lập trình Hướng đối
4
Định nghĩa lại toán tử (tiếp)
Ta định nghĩa một hàm thành viên và gán
nó cho toán tử so sánh >.
Bộ biên dịch có thể phân biệt được những
toán tử đã được định nghĩa lại bằng cách
xét kiểu dữ liệu của các toán hạng.
Định nghĩa lại toán tử chính là một hình
thức của tính đa thể.
Lập trình Hướng đối
5
Những điểm cần lưu ý
Không được phép thay đổi những chức năng
cơ bản của một toán tử hoặc thay đổi thứ tự
ưu tiên của chúng đã được định nghĩa sẵn
trong ngôn ngữ.
toán tử ++ và -- chỉ được sử dụng như những
toán tử một ngôi.
Ta có thể định nghĩa lại toán tử + dùng để
nhân 2 đối tượng. Tuy nhiên ta nên tránh điều
này vì nó làm cho chương trình khó hiểu.
Lập trình Hướng đối
6
Lợi ích
Làm cho chương trình dễ hiểu và dễ
truy tìm lỗi.
Xét ví dụ sau :
obj3 = obj1 + obj2;
cú pháp trên rõ ràng và dễ hiểu hơn cú
pháp :
obj3.addObjects( obj1, obj2);
Lập trình Hướng đối
7
Hàm toán tử
Hàm toán tử chứa các câu lệnh để
thực hiện việc định nghĩa lại một toán
tử. Ta dùng từ khoá operator theo sau
là toán tử cần định nghĩa lại :
kiểu_trả_về operator
op(danh_sách_đối_số);
trong đó op là biểu tượng của toán tử cần
định nghĩa lại như +, -, * ...
Lập trình Hướng đối
8
Toán tử một ngôi
Toán tử một ngôi chỉ có duy nhất một toán hạng :
++ toán tử một ngôi tăng
-- toán tử một ngôi giảm
Các toán tử này có thể được sử dụng theo 2 cách trước
hoặc sau. Xét ví dụ :
class Sample{
private:
int counter;
public:
Sample() {counter = 0;}
void operator ++() {++counter;}
};
Lập trình Hướng đối
9
Toán tử một ngôi (tiếp)
void main() {
Sample obj1;
obj1++; //tăng counter = 1
++obj1; //tăng counter = 2
}
Nếu bộ biên dịch tìm thấy hàm toán tử đã
được định nghĩa lại trong lớp thì câu lệnh
obj1++ được nó dịch thành obj1.operator+
+();
Lập trình Hướng đối
10
Toán tử một ngôi (tiếp)
Tương tự ta có thể định nghĩa hàm toán tử
giảm như sau :
void operator --() {--counter;}
khi đó ta có thể dùng
--obj1;
obj1--;
Trong các trường hợp trên thì hàm toán tử sẽ
được thực hiện trước tiên dù cho toán tử
đứng trước (--obj1) hay đứng sau (obj1--).
Lập trình Hướng đối
11
Toán tử một ngôi (tiếp)
Trường hợp trên không đáp ứng được yêu
cầu khi ta muốn thực hiện cả tăng và gán
trong một biểu thức như obj1 = obj2++;
Sửa lại hàm trên như sau :
Sample Sample :: void operator++() {
Sample temp;
temp.counter = ++counter;
return temp;
}
Lập trình Hướng đối
12
Sử dụng đối tượng tạm không tên
Trong ví dụ trên ta dùng đối tượng tạm temp,
ta có thể sử dụng cách khác là tạo ra một đối
tượng tạm không tên như sau :
class Sample {
private:
int counter;
public:
Sample() {counter = 0;}
Sample(int c) {counter = c;}
Sample operator++();
};
Lập trình Hướng đối
13
Sử dụng đối tượng tạm không tên
(tiếp)
Sample Sample::void operator++(){
++counter;
return Sample(counter);
}
Trong câu lệnh return trên ta dùng
constructor có một đối số để tạo ra một đối
tượng không tên với giá trị khởi tạo là giá trị
counter, sau đó hàm trả về chính đối tượng
này.
Lập trình Hướng đối
14
Sử dụng con trỏ this
Ngoài ra còn có một các nữa để trả về một
đối tượng là dùng con trỏ this.
Sample Sample::void operator++(){
++counter;
return (*this);
}
Trong trường hợp này thì không cần dùng
hàm khởi tạo có một đối số như ở trên.
Lập trình Hướng đối
15
Gia giảm trước và gia giảm sau
Nếu ++ và -- được định nghĩa lại như trên thì
bộ biên dịch sẽ cho cùng một kết quả trong hai
trường hợp sau :
obj2 = obj1++; và
obj2 = ++obj1;
Để phân biệt được trường hợp toán tử đứng sau
(gia giảm sau) ta định nghĩa hàm với 1 đối số
Sample Sample:: void operator++(int){
return Sample(counter++);
đối số giả
}
Lập trình Hướng đối
16
Toán tử hai ngôi
Toán tử hai ngôi có thể được định nghĩa
lại theo hai cách :
Như là hàm thành viên có một đối số, đối số
này chính là toán hạng bên phải của toán tử.
Ví dụ : khi gặp biểu thức obj1 + obj2 bộ biên
dịch gọi hàm operator+(Sample obj2)
Như là hàm bạn có hai đối số.
Ví dụ : operator+(Sample obj1, Sample obj2)
Lập trình Hướng đối
17
Toán tử số học hai ngôi
Có hai toán hạng tham gia thực hiện một tác
vụ.
Sample Sample::operator+(Sample a){
Sample temp;
temp.counter = counter + a.counter;
return temp;
}
Ta có thể thực hiện phép cộng hai đối tượng
như sau :
obj3 = obj1 + obj2;
Lập trình Hướng đối
18
Toán tử số học hai ngôi (tiếp)
Toán tử + có thể truy cập hai đối tượng :
Đối tượng bên trái toán tử, obj1, sẽ gọi hàm toán
tử.
Đối tượng bên phải toán tử, obj2, là đối số của lời
gọi hàm.
Ta cũng có thể thực hiện biểu thức sau :
obj4 = obj3 + obj2 + obj1;
câu lệnh trên là hợp lệ vì hàm toán tử + trả về
một đối tượng thuộc kiểu Sample.
Lập trình Hướng đối
19
Định nghĩa lại toán tử + cho chuỗi
String String::operator+(String ss){
String temp;
strcpy(temp.str, str); //copy vào biến temp
strcat(temp.str, ss.str); //nối hai chuỗi
return temp;
}
Sử dụng như sau :
String s1 = “Welcome ”;
String s2 = “to C++”;
String s3;
s3 = s1 + s2;
Lập trình Hướng đối
20
Toán tử gán phức
void Sample::operator+=(Sample a){
counter += a.counter;
}
Không cần biến tạm vì giá trị được gán cho
chính đối tượng gọi hàm (bên trái toán tử).
Hàm không có giá trị trả về.
Lập trình Hướng đối
21
Toán tử gán phức (tiếp)
Toán tử này hữu dụng trong các biểu thức như
Muốn thực hiện được các phép tính phức tạp
thì hàm cần giá trị trả về.
obj3 = obj1 +=obj2;
Ta định nghĩa lại như sau :
obj1 += obj2;
Sample Sample::operator+=(Sample a)
Với câu lệnh return đuợc viết lại :
return Sample(counter);
trả về một đối tượng không tên.
Lập trình Hướng đối
22
Toán tử so sánh
Toán tử luận lý và so sánh là toán tử hai
ngôi, nó cần hai đối tượng để so sánh. Ta có
thể định nghĩa lại các toán tử so sánh sau :
<, <=, >, >=, ==, !=
int String::operator>(String ss) {
return (strcmp(str, ss.str) > 0);
}
Lập trình Hướng đối
23
Định nghĩa lại toán tử gán
Toán tử gán mặc định chỉ đơn giản sao chép từng byte đối
tượng nguồn sang đối tượng đích.
Xét trường hợp dữ liệu thành viên là con trỏ và được cấp
phát bộ nhớ bằng new :
class String {
private:
char *str;
public:
String (char *s = “”) {
int length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
}
Lập trình Hướng đối
24
Định nghĩa lại toán tử gán (tiếp)
~String() { delete[] str;}
void display() {cout << str;}
};
void main() {
String s1(“Welcome to my world \n”);
String s2;
s2 = s1;
s1.display();
s2.display();
}
Hai đối tượng s1 và s2 được tạo. Hàm khởi tạo cấp
phát bộ nhớ và sao chép chuỗi vào đó.
Lập trình Hướng đối
25