Bài giảng Lập trình hướng đối tượng - Chương 3: Lớp và đối tượng - Trần Minh Thái

pptx 76 trang ngocly 1680
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình hướng đối tượng - Chương 3: Lớp và đối tượng - Trần Minh Thái", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pptxbai_giang_lap_trinh_huong_doi_tuong_chuong_3_lop_va_doi_tuon.pptx

Nội dung text: Bài giảng Lập trình hướng đối tượng - Chương 3: Lớp và đối tượng - Trần Minh Thái

  1. Chương 3 Lớp và đối tượng TRẦN MINH THÁI Email: minhthai@itc.edu.vn Website: www.minhthai.edu.vn Cập nhật: 08 tháng 02 năm 2015
  2. Nội dung #2 1. Khái niệm lớp và đối tượng 2. Toán tử gán 3. Phương thức thiết lập (constructor) 4. Phương thức huỷ (destructor) 5. Con trỏ this 6. Lớp template
  3. Lớp đối tượng là gì? (1/2) #3 Lớp là khái niệm trung tâm của OOP, là sự mở rộng của khái niệm cấu trúc (struct) Ngoài thành phần dữ liệu (như cấu trúc), lớp còn chứa các thành phần hàm, còn gọi là phương thức (method) hay hàm thành viên (member function)
  4. Lớp đối tượng là gì? (2/2) #4 Lớp được xem như một kiểu dữ liệu (kiểu đối tượng) Lớp giúp lập trình viên: • Trừu tượng hóa dữ liệu • Đóng gói • và ẩn thông tin
  5. Khai báo lớp (1/2) #5 Cú pháp class class_name { : member_list }; Với member_list là đặc tả các thành viên Theo quy ước, tên lớp bắt đầu bởi ký tự C
  6. Khai báo lớp (2/2) #6 Thành viên của lớp gồm ▪ Thành viên dữ liệu (data member) → thuộc tính ▪ Hàm thành viên (member function) → phương thức class CTime //Lớp thời gian { private: Thuộc tính int hour, minute, second; public: void SetTime(int h, int m, int s); Phương thức void Print(); };
  7. Khai báo lớp: thuộc tính (1/5) #7 Là dữ liệu khai báo trong lớp Cú pháp giống như khai báo biến Có thể là một đối tượng nhưng phải khác đối tượng của lớp đang định nghĩa (con trỏ hay tham chiếu: OK) Không thể vừa khai báo, vừa khởi tạo
  8. Khai báo lớp: thuộc tính (2/5) #8 class CTime //Lớp thời gian { private: int hour = 1; // Error int minute, second; public: void SetTime(int h, int m, int s); void Print(); };
  9. Khai báo lớp: phương thức (3/5) #9 Hàm khai báo trong lớp (còn gọi là giao diện) Có thể truy cập các thành viên dữ liệu và hàm thành viên khác Cài đặt trong lớp → hàm inline
  10. Khai báo lớp: phương thức (4/5) #10 Cú pháp type_name class_name :: method_name ( parameter_list ) { } Hàm inline → thêm từ khóa inline
  11. Khai báo lớp: cài đặt phương thức (5/5) #11 class CMyTime { private: int hour, minute, second; public: void SetTime(int h, int m, int s); void Print() { cout =0 && h =0 && m =0 && s<60) ? s:0; }
  12. Thuộc tính truy xuất thành viên (1/3) (member access specifier) #12 Xác định phạm vi của các thành viên của lớp có thể được sử dụng trực tiếp từ bên ngoài phạm vi lớp hay không Các thuộc tính: private, protected, public
  13. Thuộc tính truy xuất thành viên (2/3) #13 private (mặc định) Chỉ được truy xuất từ bên trong lớp protected Chỉ được truy xuất từ bên trong lớp hoặc lớp dẫn xuất của nó public (mặc định đối với thành viên của struct) Có thể được truy xuất từ bên ngoài lớp
  14. Thuộc tính truy xuất thành viên (3/3) #14 class CMyClass { !!! Các phương thức private: int x; public: tiện ích chỉ được int y; void Print() dùng bởi các phương { cout << x << "," << y << endl; thức khác trong cùng } }; lớp nên được khai void f() { báo private CMyClass m; cout<<m.x; //Error cout<<m.y; //OK m.Print(); //OK }
  15. Sử dụng lớp đối tượng (1/4) #15 Tạo đối tượng Sử dụng lớp giống như dùng struct, khai báo một biến lớp (tạo đối tượng) như khai báo biến struct Khai báo con trỏ, tham chiếu tới lớp ▪ Có thể khai báo con trỏ/ tham chiếu đến một đối tượng thuộc cùng lớp (truy cập gián tiếp) ▪ Con trỏ/ tham chiếu không là instance của lớp
  16. Sử dụng lớp đối tượng (2/4) #16 CMyTime t1; Gọi hàm thành viên CMyTime &t2=t1; của lớp → truyền CMyTime *t3 = &t1; thông điệp cho hàm t1.SetTime(12, 5, 5); thành viên đó t2.Print(); t3->SetTime(1, 5, 7); t3->Print();
  17. Sử dụng lớp đối tượng (3/4) #17 Mỗi đối tượng sẽ có một tập các dữ liệu riêng (thuộc tính) được định nghĩa trong lớp Tuy nhiên, tập các đối tượng thuộc cùng một lớp chỉ có chung duy nhất các định nghĩa hàm (phương thức)
  18. Sử dụng đối tượng (4/4) #18 CMyTime t1, t2; t1.SetTime(12, 5, 5); t2.SetTime(11,15, 15); t1.Print(); SetTime() t2.Print(); Print() hour=12 hour=11 minute=5 minute=15 second=5 second=15 Đối tượng t1 Đối tượng t2
  19. Toán tử gán (1/2) #19 Dùng để gán một đối tượng cho một đối tượng CMyTime t1; t1.SetTime(12, 5, 5); khác của cùng một lớp t1.Print(); CMyTime t2; Nó được thực hiện bởi t2 = t1; //memberwise copy t2.Print(); toán tử sao chép thành viên (memberwise copy)
  20. Toán tử gán (2/2) #20 Mỗi thành viên của một đối tượng này được sao chép riêng rẽ đến cùng một thành viên tương ứng của đối tượng khác Chú ý đối với các thành viên dữ liệu chứa vùng nhớ cấp phát động hoặc con trỏ
  21. Phương thức thiết lập (Constructor) (1/9) #21 Khởi tạo các thành viên dữ liệu khi đối tượng được tạo ra Constructor được gọi tự động mỗi khi một instance của lớp được tạo Constructor thường được dùng để khởi tạo giá trị ban đầu hoặc cấp phát động cho các thành viên dữ liệu
  22. Phương thức thiết lập (2/9) #22 Đặc điểm Constructor có cùng tên với lớp và không có giá trị trả về (kể cả void) Phải có thuộc tính truy xuất là public Constructor có thể có tham số (kể cả tham số mặc định) và đa năng hóa
  23. Phương thức thiết lập (3/9) class CMyTime #{23 private: int hour, minute, second; public: CMyTime() //(1) void CMyTime::SetTime(int h, int m, int s) { { hour=minute=second=0; hour = (h>=0 && h =0 && m =0 && s<60) ? s:0; void SetTime(int h, int m, int s); } void Print(); void main() }; { CMyTime::CMyTime(int h, int m, int s) CMyTime t1; //gọi constructor (1) { t1.Print(); SetTime(h, m, s); CMyTime t2(5,15,30);//gọi constructor (2) } t2.Print(); void CMyTime::Print() } { cout<<hour <<":"<<minute<<":"; cout<<second<<endl; }
  24. Phương thức thiết lập (4/9) #24 Có ba dạng Mặc định Sao chép Có tham số
  25. Phương thức thiết lập mặc định (5/9) (Default Constructor) #25 Không có tham số hoặc tất cả các tham số đều có giá trị mặc định Mỗi lớp có duy nhất một constructor mặc định Nếu không định nghĩa thì TBD sẽ tự tạo một constructor mặc định và constructor này rỗng (không làm gì) → Thành viên dữ liệu sẽ có giá trị “rác”
  26. Phương thức thiết lập mặc định (6/9) #26 class CMyTime class CMyTime { { private: private: int hour, minute, second; int hour, minute, second; public: public: CMyTime(); CMyTime(int h=0, int m=0, int s=0); }; };
  27. Phương thức thiết lập sao chép (7/9) (Copy Constructor) #27 Tạo ra 01 đối tượng mới giống với 01 đối tượng đã có Có 01 tham số là một tham chiếu đến 01 đối tượng của cùng lớp C++ cung cấp sẵn constructor sao chép: sao chép từng thành viên dữ liệu từ đối tượng cũ sang đối tượng mới Nếu cần thực hiện các công việc khởi tạo khác → Định nghĩa lại constructor sao chép
  28. Phương thức thiết lập sao chép (8/9) #28 class CMyTime { int hour, minute, second; public: CMyTime() CMyTime(int h, int m, int s); CMyTime t1(1,20,30); CMyTime(CMyTime & t); //Copy constructor CMyTime t2(t1); //Copy constructor CMyTime t3 = t1;//Copy constructor }; CMyTime::CMyTime(CMyTime & t) { hour = t.hour; minute = t.minute; second = t.second; }
  29. Phương thức thiết lập sao chép (9/9) #29 Toán tử gán vs Constructor sao chép Toán tử gán không tạo ra đối tượng mới: chỉ thực hiện phép gán giữa 2 đối tượng đã tồn tại Constructor sao chép được dùng để tạo một đối tượng mới và gán nội dung của một đối tượng đã tồn tại cho đối tượng mới vừa tạo
  30. Phương thức huỷ (Destructor) (1/4) #30 Tự động gọi khi đối tượng bị hủy bỏ (đối tượng rời khỏi phạm vi của nó) Đặc điểm ▪ Tên trùng với tên lớp & theo sau bởi ký tự “~” ▪ Không có tham số & không có trả về giá trị ▪ Phải có thuộc tính truy xuất là public
  31. Phương thức huỷ (2/4) #31 Đặc điểm ▪ Chỉ có duy nhất một destructor trong lớp ▪ Nếu một lớp không có định nghĩa destructor thì TBD sẽ tạo ra một destructor mặc định rỗng ▪ Destructor thường được dùng để thực hiện việc giải phòng vùng nhớ đã được cấp phát trước khi một đối tượng bị huỷ bỏ
  32. Phương thức huỷ (3/4) class CSimple #32 { int *x; public: CSimple(); //Constructor ~CSimple(); //Destructor void SetValue(int value); int GetValue(); }; CSimple::CSimple() { x = new int; //Cap phat vung nho cho x } CSimple::~CSimple() { delete x; //Giai phong vung nho x khi doi tuong bi huy bo }
  33. Phương thức huỷ (4/4) void CSimple::SetValue(int value) #33 { *x = value; } int CSimple::GetValue() { return *x; } void main() { CSimple s; int x; cout >x; s.SetValue(x); cout<<"Gia tri cua so vua nhap:"<<s.GetValue()<<endl; }
  34. Đối tượng hằng & thành viên hằng (1/7) #34 Dùng từ khóa const đối với đối tượng của một lớp để báo cho biết đối tượng đó không thể thay đổi const CMyTime t(1,20,30); t.SetTime(2,20,30); // Error t.Print(); //Error???
  35. Đối tượng hằng & thành viên hằng (2/7) #35 Khi một đối tượng là hằng, một số TBD cấm hoàn toàn gọi bất kỳ hàm thành viên nào của đối tượng đó, một số TBD thì đưa cảnh báo (warning) Để khắc phục, có thể khai báo các hàm thành viên là hằng → Để đảm bảo nó không sửa đổi đối tượng
  36. Đối tượng hằng & thành viên hằng (3/7) #36 class CMyTime { private: int hour; int minute; int second; public: CMyTime(int h=0, int m=0, int s=0); CMyTime(CMyTime & t); void SetTime(int h, int m, int s); void SetHour(int h); //Thiet lap hour void SetMinute(int m); //Thiet lap minute void SetSecond(int s); //Thiet lap second //Cac ham get int GetHour() const; //Tra ve hour int GetMinute() const; //Tra ve minute int GetSecond() const; //Tra ve second void Print() const; };
  37. Đối tượng hằng & thành viên hằng (4/7) CMyTime::CMyTime(int h, int m, int s) void CMyTime::SetSecond(int s) { { #37SetTime(h, m, s); second=(s>=0 && s =0 && h =0 && m =0 && s =0 && h =0 && m<60)?m:0; }
  38. Đối tượng hằng & thành viên hằng (5/7) #38 Hàm thành viên hằng không được thay đổi đối tượng (tức là không sửa đổi giá trị của thành viên dữ liệu) Hàm thành viên hằng có thể được đa năng hóa bởi phiên bản không hằng
  39. Đối tượng hằng & thành viên hằng (6/7) #39 Thành viên dữ liệu hằng Phải cung cấp một bộ khởi tạo thành viên (member initializer) cho constructor để khởi tạo giá trị ban đầu cho thành viên dữ liệu đó Nếu có nhiều thành viên dữ liệu hằng thì phải cung cấp nhiều bộ khởi tạo thành viên cách nhau bởi dấu phẩy
  40. Đối tượng hằng & thành viên hằng (7/7) #40 class CMyClass { CMyClass::CMyClass(int c, int a): x(a), y(a) private : { int count; count = c; const int x ,y; } public: CMyClass (int c = 0, int a = 1); };
  41. Thành viên là đối tượng của lớp khác (1/5) #41 Khi một đối tượng đi vào phạm vi, constructor của nó được gọi một cách tự động → Cần phải mô tả các tham số được truyền tới các constructor của đối tượng thành viên Các đối tượng thành viên được xây dựng theo thứ tự mà chúng được khai báo và trước đối tượng của lớp chứa chúng
  42. Thành viên là đối tượng của lớp khác (2/5) #42 class CDate CDate::CDate (int d, int m, int y) { { int day, month, year; month=((m >=1)&&( m<=12))?m:1; int TestDay (int d); year=y; public: day=TestDay (d); CDate ( int d =1 , int m=1, int y=1900); } void Print() const; }; void CDate::Print () const { cout << day<< "/"<<month<<"/"<<year; }
  43. Thành viên là đối tượng của lớp khác (3/5) #43 int CDate::TestDay (int d) { static int days[]={31,28,31,30,31,30,31,31,30,31,30,31}; if ((d>0) && (d<=days [ month -1])) return d; if ((month==2) && (d==29)) if (( year % 400==0) || ((year % 10) && (year % 4==0))) return d; return 1; } class CPerson { private: char name[50]; CDatebirth_day; CDate work_day; public : CPerson (char *st,int bday,int bmonth,int byear,int wday,int wmonth,int wyear); void Print () const; };
  44. Thành viên là đối tượng của lớp khác (4/5) #44 CPerson::CPerson (char *st,int bday,int bmonth,int byear,int wday,int wmonth,int wyear) : birth_day (bday, bmonth, byear), work_day (wday, wmonth, wyear) { strcpy (name, st); } void CPerson ::Print () const { cout <<"name:"<<name<<endl; cout <<"birth day:"; birth_day.Print (); cout <<endl<<"work day:"; work_day.Print (); }
  45. Thành viên là đối tượng của lớp khác (5/5) #45 Một đối tượng thành viên cần được khởi tạo thông qua một bộ khởi tạo thành viên Nếu một bộ khởi tạo thành viên không được cung cấp thì constructor mặc định của đối tượng thành viên sẽ được gọi một cách tự động
  46. Hàm friend (1/4) #46 Được định nghĩa bên ngoài phạm vi của lớp đó → Không phải là phương thức của lớp Có quyền truy cập đến các thành viên private và protected
  47. Hàm friend (2/4) #47 Cú pháp: thêm từ khóa friend phía trước khai báo prototype của hàm trong định nghĩa lớp Phân loại: ▪ Hàm toàn cục (hàm tự do) ▪ Hàm thành viên
  48. Hàm friend: Hàm toàn cục (3/4) class CCount #48 { private: int x ; public: CCount () { x = 0; } friend void SetX (CCount &c, int val) ; }; void SetX (CCount &c, int val) { c.x = val; }
  49. Hàm friend: Hàm thành viên (4/4) #49 class A; //Khai báo lớp A class B { int fa(A x); }; class A { friend int B::fa(A x); };
  50. Lớp friend #50 Một lớp là friend của một lớp khác thì tất cả các hàm thành viên của lớp này đều là friend của lớp đó class B; Cú pháp: class A { friend class_name; friend class B; };
  51. Con trỏ this (1/6) #51 Xác định chính xác đối tượng nào đang gọi để nó truy cập đến đúng dữ liệu của đối tượng tương ứng Mỗi đối tượng có một con trỏ this lưu địa chỉ của đối tượng (trỏ đến chính nó) để khi mỗi lần gọi một hàm → this được truyền cho hàm đó để hàm biết chính xác đối tượng nào đang gọi
  52. Con trỏ this (2/6) #52 Khi nào sử dụng con trỏ this? Khi trong phương thức muốn trả về đối tượng đang gọi hàm Phương thức cần lưu lại đối tượng cũ trước khi thay đổi dữ liệu Để phân biệt thành viên dữ liệu trong đối tượng với biến của riêng hàm khi trùng tên
  53. Con trỏ this (3/6) #53 class A class A { { private: private: int x; int x; public: public: A(int a); A(int x); Trùng tên (? phân biệt) }; }; A::A(int a) A::A(int x) { { this -> x = a; this -> x = x; } }
  54. Con trỏ this (4/6) class CMyTime #54 { private: int hour; int minute; int second; public: CMyTime(int h=0, int m=0, int s=0); CMyTime(CMyTime & t); CMyTime & SetTime(int h, int m, int s); CMyTime & SetHour(int h); //Thiet lap hour CMyTime & SetMinute(int m); //Thiet lap minute CMyTime & SetSecond(int s); //Thiet lap second //Cac ham get int GetHour() const; //Tra ve hour int GetMinute() const; //Tra ve minute int GetSecond() const; //Tra ve second void Print() const; }; CMyTime::CMyTime(int h, int m, int s) { SetTime(h, m, s); }
  55. Con trỏ this (5/6) CMyTime::CMyTime(CMyTime & t) { #55 hour=t.hour; minute=t.minute; second=t.second; } CMyTime & CMyTime::SetTime(int h, int m, int s) { hour=(h>=0 && h =0 && m =0 && s =0 && h =0 && m<60)?m:0; return *this; }
  56. Con trỏ this (6/6) CMyTime & CMyTime::SetSecond(int s) {#56 second=(s>=0 && s<60)?s:0; return *this; } int CMyTime::GetHour() const { return hour; CMyTime t; } int CMyTime::GetMinute() const t.SetHour(18).SetMinute(30).SetSecond(0); { return minute; } int CMyTime::GetSecond() const { return second; } void CMyTime::Print() const { cout<<hour<<":"<<minute<<":"; cout<<second<<endl; }
  57. Thành viên tĩnh (static) (1/5) #57 Mỗi đối tượng của một lớp có bản sao chép của chính nó cho tất cả các thành viên dữ liệu của lớp Muốn có duy nhất một bản sao thành viên dữ liệu dùng chung cho tất cả các đối tượng của lớp →Thành viên dữ liệu tĩnh: Có thể xem là biến toàn cục nhưng nằm trong một lớp nào đó (độc lập với các đối tượng)
  58. Thành viên tĩnh (2/5) #58 Hàm thành viên muốn truy cập đến thành viên dữ liệu tĩnh thì hàm thành viên đó phải là hàm thành viên tĩnh Các thành viên tĩnh có thể tồn tại ngay cả khi không có đối tượng của lớp đó Muốn thành viên của lớp là thành viên tĩnh, thêm từ khóa static phía trước
  59. Thành viên tĩnh (3/5) #59 Thành viên dữ liệu tĩnh phải được khởi tạo trước khi sử dụng. Được khởi tạo bên ngoài lớp theo cú pháp: type class_name :: static_data_member_name = value; Để truy cập thành viên tĩnh không thông qua đối tượng, thêm vào đầu tên lớp và toán tử định phạm vi ▪ class_name :: static_data_member_name ▪ class_name :: method_name (parameters)
  60. Thành viên tĩnh (4/5) #60 Các hàm thành viên không tĩnh có thể gọi hàm thành viên tĩnh nhưng ngược lại không được Do hàm thành viên tĩnh là hàm độc lập với bất kỳ đối tượng nào → Không có con trỏ this Hàm thành viên tĩnh không được là hàm thành viên hằng
  61. Thành viên tĩnh (5/5) class CChicken { #61 private: static int count; void main() public: { CChicken(); CChicken::CounterDisplay(); ~CChicken(); CChicken u; static void CounterDisplay(); u.CounterDisplay(); }; { int CChicken::count = 0; //Khởi động CChicken v; CChicken::CChicken() v.CounterDisplay(); { } ++count; CChicken::CounterDisplay(); } CChicken t; CChicken::~CChicken() t.CounterDisplay(); { } count; } void CChicken::CounterDisplay() { cout<<"Hien co "<<count<<" con ga\n"; }
  62. Mảng các đối tượng #62 CMyTime t1[2];//Gọi constructor mặc định t1[0].Print(); t1[1].Print(); //Gọi constructor có tham số CMyTime t2[2]={CMyTime(3,4,5),CMyTime(4,5,6)}; t2[0].Print(); t2[1].Print();
  63. Cấp phát động cho đối tượng (1/2) #63 Các đối tượng có thể cấp phát động giống như các dữ liệu khác thông qua các toán tử new và delete Toán tử new tự động gọi hàm constructor và toán tử delete tự động gọi destructor
  64. Cấp phát động cho đối tượng (2/2) #64 CMyTime *t1,*t2; t1 = new CMyTime(); //t1 = new CMyTime; t2 = new CMyTime(9,30,30); t1->Print(); t2->Print(); delete t1; delete t2; CMyTime *t = new CMyTime[10]; delete [] t;
  65. Con trỏ xác định thành viên của lớp & đối tượng (1/2) #65 Khai báo một con trỏ trỏ đến thành viên của lớp class_name ::* pointer_name; Lấy địa chỉ của một thành viên & class_name :: member_name Truy cập tới thành viên khi đối tượng của lớp & ->* thành viên của đối tượng đều xác định bởi con trỏ Được sử dụng khi đối tượng được chỉ ra trực tiếp .* còn các thành viên của nó thì được xác định nhờ con trỏ
  66. Con trỏ xác định thành viên của lớp & đối tượng (2/2) #66 class A A a; { int A::*px; //con trỏ trỏ đến thành viên x public: void (A::*pf)(); //con trỏ trỏ đến thành viên Print() int x; void Print() px = &A::x; { pf = &A::Print; cout x = 20; cout *px *pf)();
  67. Lớp template (1/9) #67 Lớp đặc biệt được tham số sao cho chúng thể hiện một họ các lớp Lớp này cũng mang đầy đủ ý tưởng của hàm template Định nghĩa ▪ template class_declaration ▪ template class_declaration
  68. Lớp template (2/9) #68 Định nghĩa hàm thành viên type_name class_name :: method_name ( parameters ) { } Sử dụng class_name object_name ;
  69. template Lớp template (3/9) class CMyPoint { #69 private: T x,y; CMyPoint p(10,2); public: p.Display(); CMyPoint(T x=0, T y=0); void Display(); CMyPoint *q, *r, *s; }; q=new CMyPoint ; template CMyPoint ::CMyPoint(T x, T y) r=new CMyPoint (); { this->x=x; s=new CMyPoint [10]; this->y=y; } template delete q; void CMyPoint ::Display() delete r; { delete [] s; cout<<"Toa do:["<<x<<","<<y<<"]"<<endl; }
  70. Lớp template (4/9) #70 Giống như hàm template, các lớp template cũng có các tham số kiểu và tham số biểu thức template template class CTable void CTable ::Fun() { { private: T a[n] } public: CTable x; void Fun(); };
  71. Lớp template (5/9) #71 Tham số thực tương ứng với tham số biểu thức phải là hằng số Hai lớp thực thể của cùng lớp template tương ứng với cùng một kiểu nếu ▪ Các tham số kiểu tương ứng nhau một cách chính xác ▪ Các tham số biểu thức có cùng giá trị
  72. Lớp template (6/9) #72 template class CTable { private: CTable t1; T a[n] CTable t2; t2 = t1; //Error public: CTable t3; void Fun(); CTable t4; }; t4 = t3; //Error CTable t5; template CTable t6; void CTable ::Fun() t6 = t5; //OK { } CTable x;
  73. Lớp template (7/9) #73 Các lớp template và hàm template cũng cho phép khai báo friend Hàm và lớp thông thường là friend template class CMyClass { friend class A; friend int Fun(int); };
  74. Lớp template (8/9) #74 template class CMyPoint { Một thực thể của hàm } ; template template và lớp template int F(T a) { là friend } template class CMyClass { friend class CMyPoint ; friend int F(float); };
  75. Lớp template (9/9) #75 template class CMyPoint Hàm template và lớp { template là friend } ; template int F(T a) { } template class CMyClass { template friend class CMyPoint ; template friend int F(U); };
  76. Q&A #76