Giới thiệu 1/3 Đa năng hóa toán tử là khả năng của C++ cho phép định nghĩa lại toán tử +, -, *, / , … trên kiểu dữ liệu khác Chương trình ngắn gọn, dễ đọc và có ý nghĩa hơn so với việ
Trang 2Nội dung
1 Giới thiệu
2 Cách cài đặt & sử dụng đa năng hoá toán tử
3 Một số kỹ thuật đa năng hoá toán tử đặc biệt
Trang 3Giới thiệu (1/3)
Đa năng hóa toán tử là khả năng của C++ cho phép định nghĩa lại toán tử (+, -, *, / , …) trên kiểu dữ liệu khác Chương trình ngắn gọn, dễ đọc và có
ý nghĩa hơn so với việc gọi hàm bình thường
Đa năng hóa toán tử bằng cách định nghĩa hoạt động của từng toán tử giống như định nghĩa một hàm hàm toán tử
Trang 4 Hàm toàn cục (hàm tự do) hàm friend
Hàm thành viên của lớp (hàm non-static) có thuộc tính truy xuất public
Trang 5Giới thiệu (3/3)
Cú pháp khi gọi Khai báo
Hàm thành viên Hàm toàn cục
aa#bb aa .operator # ( bb ) operator # ( aa , bb )
Với # là ký hiệu dấu toán tử
Trang 6Các lưu ý (1/)
Không thể định nghĩa toán tử mới
Phần lớn các toán tử được đa năng hóa ngoại trừ các toán tử sau:
.* :: ?: typeid sizeof const_cast dynamic_cast reinterpret_cast static_cast
Không thể đa năng hóa ký hiệu tiền xử lý
Không thể thay đổi độ ưu tiên của toán tử hay số các toán hạng của nó
Trang 7Các lưu ý (2/)
Không thể thay đổi ý nghĩa của toán tử khi áp dụng các kiểu cài sẵn
Không dùng tham số có giá trị mặc định
Các toán tử: = [] () -> đòi hỏi hàm toán tử phải
là hàm thành viên
Phải chủ động định nghĩa toán tử += -= *= /= dù
đã định nghĩa + - * /
Trang 8Tham số (1/2)
Số lượng các tham số của hàm toán tử phụ thuộc:
Toán tử một ngôi hay hai ngôi
Toán tử được khai báo là hàm toàn cục hoặc hàm thành viên
Trang 9Tham số (2/2)
Nên sử dụng tham chiếu khi có thể (đối tượng lớn) Luôn sử dụng tham số hằng tham chiếu nếu tham
số không bị sửa đổi
bool CComplex::operator == (const CComplex & c) const;
Hàm thành viên nên khai báo là hàm thành viên hằng nếu toán hạng đầu tiên không bị sửa đổi
Các toán tử tính toán/ so sánh thường dùng hằng tham chiếu
Trang 11Ví dụ hàm toán tử thành viên (1/3)
class CComplex{
void Println() const;
CComplex operator + (CComplex c) const;
CComplex operator + (double r) const;
Trang 12CComplex tmp;
tmp.real = real + c.real;
tmp.image = image + c.image;
return tmp;
}CComplex CComplex::operator + (double r) const{
Trang 13c2.Print(), cout<<"+ 4.5 =", c4.Println();
c3.Print(), cout<<"+", c4.Print(),cout<<"=",
(c3 + c4).Println(); //OK
c4 = 4.5 + c1; //Error vì 4.5 không phải là đối tượng của CComplex
Trang 14CComplex (double r, double i);
CComplex (double r); //constructor chuyển kiểu: double CComplex
CComplex (const CComplex &c ); //constructor sao chépvoid Print() const;
void Println() const;
friend CComplex operator + ( CComplex c1, CComplex c2 );friend CComplex operator - ( CComplex c1, CComplex c2 );
CComplex operator += (CComplex c);
friend bool operator == (CComplex c1, CComplex c2);
};
Trang 15Ví dụ hàm toán tử friend (2/5)
CComplex::CComplex(){
real = image = 0.0;
}CComplex::CComplex(double r, double i){
real = r;
image = i;
}CComplex::CComplex(double r) {
real = r;
image= 0.0;
}CComplex::CComplex(const CComplex &c){
real = c.real;
image = c.image;
}
Trang 16Print(), cout<<endl;
}CComplex operator + (CComplex c1, CComplex c2){
CComplex tmp;
tmp.real = c1.real + c2.real;
tmp.image = c1.image + c2.image;
return tmp;
}
Trang 17tmp.real = c1.real - c2.real;
tmp.image = c1.image - c2.image;
Trang 18c1.Print(), cout<<"+", c2.Print(),cout<<"=", c3.Println();
c4 = c2 + 4.5; //OK: c4 = operator + (c2,CComplex(4.5))c2.Print(), cout<<"+ 4.5 =", c4.Println();
c3.Print(), cout<<"+", c4.Print(),cout<<"=",
(c3 + c4).Println(); //OK
c4 = 4.5 + c1; //OK: c4 = operator + (CComplex((4.5), c1)
cout<<"4.5+", c1.Print(), cout<<"=", c4.Println();
c4.Print(), cout<<" += ", c3.Print();
cout<<" -> Ket qua:", c4.Println();
Trang 19CComplex operator += (CComplex c);
CComplex operator - ();
};
Trang 20Ví dụ đa năng hoá toán tử 1 ngôi (2/2)
CComplex CComplex::operator-(){
Trang 21Ví dụ đa năng hoá toán tử đặc biệt (1/3)
Gồm các toán tử:
() [] ++ , = ->
Toán tử chuyển kiểu
Toán tử new và delete
Toán tử [] và ()
[]: chỉ có hai tham số
(): số các tham số bất kỳ
Trang 22delete [] data;
}
Trang 23Ví dụ đa năng hoá toán tử đặc biệt (3/3)
int & CVector::operator[](int i)
Trang 24Đa năng hoá toán tử tăng/ giảm (1/2)
Tiền tố và hậu tố phân biệt qua số ngôi
Tiền tố là toán tử một ngôi
Hậu tố là toán tử hai ngôi (tham số thứ hai kiểu int)
Trang 25++y;
return *this;
}CMyPoint CMyPoint::operator ++(int){
return operator ++();
}
Trang 26{
return this;
}}
CMyClass m ;cout<<m.data<<endl;cout<<m->data;
Trang 27private:
int x, y;
public:
CMyPoint(int a = 0 , int b = 0 ){
x = a;
y = b;
}CMyPoint operator ,(CMyPoint);
};
CMyPoint CMyPoint :: operator , (CMyPoint p){
CMyPoint tmp ;tmp.x = p.x ;
tmp.y = p.y ;return tmp ;}
Trang 28int *data;
public:
A(int x) {
data = new int(x);
}
~A() {
delete data;
} void Print(char *st) {
cout<<st<<"data:"<<*data<<endl;
} };
Trang 30Đa năng hoá toán tử gán (1/3)
Đa năng hóa toán tử gán chỉ cần thiết khi
Sao chép có xử lý bên trong (deep copy) – chẳng hạn sử dụng bộ nhớ động
Sao chép đòi hỏi cả tính toán – chẳng hạn gán một số hiệu có giá trị duy nhất/ tăng số đếm, …
Hàm toán tử của toán tử gán phải trả về một tham chiếu của đối tượng bên phải của phép gán
Trang 31str = strdup(s);
} CMyString(const CMyString &s)
{
str = strdup(s.str);
}
~CMyString() {
free(str);
} CMyString & operator = (const CMyString
Trang 32Đa năng hoá toán tử gán (3/3)
CMyString & CMyString::operator = (const CMyString &s)
{
if (this != &s)
{
free(str); //giải phóng vùng nhớ cũ str = strdup(s.str); //sao chép mới }
return *this;
}
CMyString s1("AAAA"); CMyString s2 = s1;
Trang 33Đa năng hoá toán tử new & delete (1/3)
Thay thế hẳn các toán tử này Hàm toàn cục
Chỉ áp dụng cho đối tượng của một lớp Hàm thành viên của lớp
Cú pháp
void * operator new(size_t size);
void operator delete(void * ptr);
Trang 34Đa năng hoá toán tử new & delete (2/3)
void * operator new(size_t size) {
return malloc(size);
} void operator delete(void *ptr) {
x=a;
y=b;
} void Print() const {
cout<<"["<<x<<","<<y<<"]"<<endl;
} };
Trang 35} void * operator new(size_t size) {
cout<<"Toan tu new cua lop"<<endl;
return ::new unsigned char[size];
cout<<"x:"<<x<<endl; }
delete n;
delete x;
Trang 36Toán tử chuyển kiểu (1/5)
Có hai toán tử chuyển kiểu
Ngầm định: sử dụng constructor chuyển kiểu (có một tham số duy nhất là tham số kiểu cần chuyển đổi)
Tường minh: sử dụng toán tử chuyển kiểu
Trang 37… CComplex c = 6.5; //Chuyển kiểu ngầm định
…
Trang 38Toán tử chuyển kiểu (3/5)
Nếu muốn ngăn chặn chuyển kiểu tự động, dùng từ khoá explicit khi khai báo constructor
… CComplex c = CComplex(6.5);
…
Trang 39Toán tử chuyển kiểu (4/5)
Toán tử chuyển kiểu
Là thành viên của lớp cần chuyển kiểu
Toán tử này không có kiểu trả về, có tên là tên của kiểu chuyển đổi và không có tham số
Cú pháp
operator type_name ( );
Trang 40data=f;
}operator double(){
return data;
}operator int(){
return (int)data;
}};
Trang 41return a+b;
}
int a = 5, b = 7;
cout<<Sum(a,b)<<endl; //OKdouble x = 4.5, y =6.8;
cout<<Sum(x,y )<<endl; //OKcout<<Sum(a,x)<<endl; //ambiguity: int Sum(int,int) or
//double Sum(double, double)
Trang 42}operator double(){
return data/2.0;
}friend CNumber operator + (CNumber n1, CNumber n2);
Trang 43Đa năng toán tử << và >> (1/)
<< và >> là hai toán tử thao tác trên bit số nguyên C++ định nghĩa << và >> dùng các đối tượng thuộc lớp ostream & istream để thực hiện các thao tác xuất/ nhập
Lớp ostream định nghĩa toán tử << áp dụng cho các kiểu dữ liệu cơ bản (nguyên, thực, …)
Lớp istream định nghĩa toán tử >> áp dụng cho các kiểu dữ liệu cơ bản
Trang 44Đa năng toán tử << và >> (2/)
Dùng tham chiếu ostream đa năng toán tử <<
để xuất dòng dữ liệu cho lớp đang định nghĩa Dùng tham chiếu istream đa năng toán tử >>
để nhập dòng dữ liệu cho lớp đang định nghĩa
Trang 45friend ostream& operator << (ostream &os, CComplex
&c);
};
Trang 46Đa năng toán tử << và >> (4/)
istream& operator >> (istream& is, CComplex& c)
cout<<"So phuc vua nhap:"<<c;
Trang 47Q&A