Trang 2 trên tổng số 3 Đầu tiênĐầu tiên 123 Cuối cùngCuối cùng
Từ 11 tới 20 trên tổng số 30 kết quả

Đề tài: Lập trình C++ | Tài liệu lập trình C++ của Phạm Văn Ất

  1. #11
    Ngày gia nhập
    12 2006
    Bài viết
    72

    Mặc định Lập trình C++ | Tài liệu lập trình C++ của Phạm Văn Ất

    §5. CÁC HÀM TRỰC TUYẾN (INLINE)
    5.1. Các ưu nhược điểm của hàm
    Viêc tổ chức chương trình thành các hàm có 2 ưu điểm :
    thứ nhất:chia chương trình thành các đơn vị độc lập, làm cho chương trình được tổ chức một cách khoa học, dễ kiểm soát, dễ phát hiện lỗi,dễ phát triển, mở rộng.
    thứ hai: giảm được kích thước chương trình,vì mỗi đoạn chương trình thực hiện một nhiệm vụ của hàm được thay bằng lời gọi hàm.
    Tuy vậy hàm cũng có nhược điểm là làm cho tốc độ chương trình chậm hơn do phải thực hiện một số thao tác có tính thủ tục mỗi khi gọi hàm như: Cấp phát vùng nhớ cho các đối và các biến cục bộ, truyền dữ liệu của các tham số cho các đối, giải phóng vùng nhớ trước khi thoát khỏi hàm.
    Các hàm trực tuyến inline có khả năng khắc phục được nhược điểm trên.
    5.2. Các hàm trực tuyến
    Để biến một hàm thành trực tuyến ta viết them từ khoá
    inline
    vào trước khai báo nguyên mẫu của hàm. Nếu không dung nguyên mẫu thì viết từ khoá này trước dòng đầu tiên của định nghĩa hàm. Ví dụ:
    inline float f(int n,float x);
    float f(int n,float x)
    {
    //Các câu lẹnh trong than hàm
    }
    hoặc
    inline float f(int i,float x)
    {
    //các câu lệnh trong than hàm
    }
    Chú ý: Trong mọi trường hợp,từ khoá inline phải xuất hiện trước lời gọi hàm thì trình biên dịch mới biết cần xử lí hàm theo kiểu inline.
    Ví dụ: hàm f trong chương trình sau sẽ không phải là hàm trực tuyến vì từ khoá inline được viết sau lời gọi hàm:
    #include<iostream.h>
    #include<conio.h>
    void main()
    {
    int s;
    s= f(5,6);
    cout<<s;
    getch();
    }
    Inline int f(int a,int b)
    {
    return a*b;
    }

    Chú ý: Trong C++,nếu hàm được xây dựng sau lời gọi hàm thì bắt buộc phải khai báo nguyên mẫu hàm trước lời gọi hàm. Trong ví dụ trên trình biên dịch C++ sẽ bắt lỗi do thiếu khai báo nguyên mẫu hàm f.
    5.3. Cách biên dịch hàm trực tuyến
    Chương trình dịch sẽ xử lí các hàm inline như các macro(được định nghĩa trong lệnh #define), nghĩa là nó sẽ thay đổi mỗi lời gọi hàm bằng một đoạn chương trình thực hiện nhiệm vụ của hàm. Cách này làm cho chương trình dài ra nhưng tốc độ chương trình tăng lên do không phải thực hiện các thao tác mang tính thủ tục gọi hàm.
    5.4. So sánh macro với hàm trực tuyến
    Dung macro và hàm trực tuyến đều dẫn đến các kết quả tương tự, tuy nhiên người ta thích dung hàm trực tuyến hơn,vì cách này bảo đảm tính cấu trúc của chương trình,dễ sử dụng và tránh được các sai sót lặt vặt thường gặp khi dung #define ( như thiếu các dấu ngoặc,dấu chấm phẩy)
    5.5. Khi nào thì nên dung các hàm trực tuyến
    Phương án dung hàm trực tuyến rút ngắn được thời gian chạy máy nhưng lại làm tăng khối lượng bộ nhớ chương trình (nhất là đối với các hàm trực tuyến có nhiều câu lệnh). Vì vậy chỉ nên dung phương án hàm trực đối với các hàm nhỏ.
    5.6. Sự hạn chế của trình biên dịch
    Không phải khi gặp từ khoa inline là trình biên dịch nhất thiết phải xử lí hàm theo kiểu trực tuyến
    Chú ý : từ khoá inline chỉ là một sự gợi ý đối với trình biên dịch chứ không phải là một mệnh lệnh bắt buộc
    Có một số hàm trình biên dịch thường không xử lí theo kiểu inline như các hàm chứa biến static, hàm chứa các lệnh chu trình hoặc lệnh goto, lệnh swich, hàm đệ quy. Trong các trườn hợp này hàm inline lẽ dĩ nhiên bị bỏ qua 
    Thậm chí các hàm inline còn bị bỏ qua đối với các hàm không có những hạn chế kể trên nếu như trình biên dịch “cảm thấy” không cần thiết (ví dụ như quá nhiều hàm inline làm cho bộ nhớ chương trình trở nên quá lớn)
    Ví dụ: Chương trình sau sử dụng hàm inline tính chu vi,diện tích của hình chữ nhật
    Phương án 1: Không khai báo nguyên mẫu. Khi đó hàm dtcvhcn phải đặt trong hàm main.
    Code:
    #include<conio.h>
    #include<iostream.h>
    inline void dtcvhcn(int a,int b,int cv,int &dt,int &cv)
      {
        dt=a*b;
        cv=2*(a+b);
     }
    void main()
       {
         int a[20],b[20],cv[20],dt[20],n;
         cout<<”\n so hinh chu nhat: “;
         cin>>n;
         for(int i=1;i<=n;i++)
           {
             cout<<”\n\n nhap hai canh cua hinh chu nhat thu “<<i<< “:”;
             cin>>a[i]>>b[i];
             dtcvhcn(a[i],b[i],dt[i],cv[i]);
          }
        clrscr();
        for(i=1;i<=n;i++)
          {
            cout<<”\n\n hinh chu nhat thu “<<i<<”:”;
            cout<<”\n\n do dai 2 canh = “<<a[i]<<” va ”<<b[i];
            cout<<”\n\n dien tich : “<<dt[i];
            cout<<”\n\n chu vi: “<<cv[i];
         }
      getch();
    }
    Phương án 2: Sử dụng khai báo nguyên mẫu. Khi đó từ khoá inline đặt trước nguyên mẫu.
    Chú ý: không được đặt inline trước định nghĩa hàm. Trong chương trình dưới đây nếu đặt inline trước định nghĩa hàm thì hậu quả như sau:
    Chương trình vẫn dịch bình thường, nhưng khi chạy thì chương trình bị quẩn , không thoát ra được.
    Code:
    #include<conio.h>
    #include<iostream.h>
    inline void dtcvhcn(int a,int b,int cv,int &dt,int &cv);
    void main()
       {
         int a[20],b[20],cv[20],dt[20],n;
         cout<<”\n so hinh chu nhat: “;
         cin>>n;
         for(int i=1;i<=n;i++)
           {
             cout<<”\n\n nhap hai canh cua hinh chu nhat thu “<<i<< “:”;
             cin>>a[i]>>b[i];
             dtcvhcn(a[i],b[i],dt[i],cv[i]);
          }
        clrscr();
        for(i=1;i<=n;i++)
          {
            cout<<”\n\n hinh chu nhat thu “<<i<<”:”;
            cout<<”\n\n do dai 2 canh = “<<a[i]<<” va ”<<b[i];
            cout<<”\n\n dien tich : “<<dt[i];
            cout<<”\n\n chu vi: “<<cv[i];
         }
      getch();
     }
    void dtcvhcn(int a,int b,int cv,int &dt,int &cv) 
    {
        dt=a*b;
        cv=2*(a+b);
     }

  2. #12
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §6. ĐỊNG NGHĨA CHỒNG CÁC HÀM (OVERLOADING)
    6.1. Khái niệm về định nghĩa chồng
    Định nghĩa chồng (hay còn gọi là sự tải bội) các hàm là dung cùng một tên để định nghĩa các hàm khác nhau. Đây là mở rộng rất có ý nghĩa của C++.
    Như đã biết, trong C và các ngôn ngữ khác (như PASCAL, FOXPRO..) mỗi hàm đều phải có một tên phân biệt. Đôi khi đây là sự hạn chế lớn, vì phải dung nhiều hàm khác nhau để giải quyết cùng một công việc. Ví dụ để lấy giá trị tuyệt đối trong C cần dung tới 3 hàm khác nhau
    int abs(int i);// lấy giả trị tuyệt đối giá trị kiểu int
    long labslong l);// lấy giá trị tuyệt đối giá trị kiểu long
    double fabs(double d)// lấy giá trị tuyệt đối giá trị kiểu double
    Nhờ khả năng định nghĩa chồng, trong C++ có thể dung chung một tên cho cả 3 hàm như sau:
    int abs(int i);//lấy giá trị tuyệt đối giá trị kiểu int
    long abs(long l);//lấy giá trị tuyệt đối giá trị kiểu long
    double abs(double d);//lấy giá trị tuyệt đối gtrị kiểu double
    6.2. Yêu cầu về các hàm định nghĩa chồng
    Khi dung cùng một tên để định nghĩa nhiều hàm, trình biên dịch C++ sẽ dựa vào sự khác nhau về tập đối của các hàm này để đổi tên các hàm. Như vậy sau khi biên dịch mỗi hàm sẽ có một tên khác nhau
    Từ đó cho thấy: các hàm được định nghĩa trùng tên phải có tập đối khác nhau( về số lượng hoặc kiểu). Nếu 2 hàm hoàn toàn trùng tên và trùng đối thì trình biên dịch sẽ không có cách nào phân biệt được. Ngay cả khi 2 hàm này có kiểu khác nhau thì trình biên dịch vẫn báo lỗi. Ví dụ khi xây dựng 2 hàm có cùng tên là f và có cùng một đối nguyên a, nhưng kiểu hàm khác nhau. Hàm thứ nhất kiểu nguyên (trả về a*a), hàm thứ 2 kiểu void (in giá trị a). Chương trình sẽ bị thông báo lỗi khi biên dịch
    Code:
    #include<conio.h>
    #include<iostream.h>
    int f(int a);
    void f(int a);
    int f(int a)
       {
         return a*a;
      }
    void f(int a)
      {
        cout<<”\n\n”<<a;
      }
    void main()
       {
         int b=f(5);
         f(b);
         getch();
      }
    6.3. Sử dụng các hàm định nghĩa chồng
    Khi gặp một lời gọi, trình biên dịch sẽ căn cứ và số lượng và kiểu của các tham số để gọi hàm có đúng tên và đúng bộ đối số tương ứng
    Ví dụ:
    abs(123);// tham số kiểu int, gọi hàm int abs(int i)
    abs(123L);//tham số kiểu long,gọi hàm long abs(long i)
    abs(3.14);//tham số kiểu double,gọi hàm double abs(double i);
    Khi không có hàm nào có bộ đối cùng kiểu với bộ tham số( trong lời gọi), thì trình biên dịch sẽ chọn hàm nào có bộ đối gần kiểu nhất( phép chuyển kiểu dễ dàng nhất)
    Ví dụ:
    abs(‘A’);// tham số kiểu char,gọi hàm int abs(int i);
    abs(3.14F);// tham số kiểu float,gọi hàm double abs(double d);
    6.4. Nên sử dụng phép định nghĩa chồng các hàm như thế nào?
    Như đã nói ở trên, khi xây dựng cũng như sử dụng các hàm trùng tên, trình biên dịch C++ đã phải suy đoán và giải quyết nhiều trường hợp khá nhập nhằng. Vì thế không nên lạm dụng quá đang khả năng định nghĩa chồng, vì điều đó làm cho chương trình khó kiểm soát, dễ dẫn đến sai sót. Việc sử dụng định nghĩa chồng hàm sẽ có hiệu quả nếu sử dụng theo các lời khuyên sau:
    + Chỉ nên định nghĩa chồng các hàm để thực hiện các công việc như nhau nhưng trên các đối tượng dữ liệu khác nhau. Ví dụ trong chương trình xây dựng các hàm: cộng 2 ma trận chữ nhật kiểu double, cộng hai ma trận chữ nhật kiểu int,cộng hai ma trận vuông kiểu double,cộng hai ma trận vuông kiểu int, thì 4 hàm trên nên định nghĩa chồng (đặt cùng tên).
    + Nên dung các phép chuyển kiểu (nếu cần) để bộ tham số trong lời gọi hàm hoàn toàn trùng kiểu với bộ đối số của một hàm được định nghĩa chồng. Vì như thế tránh được sựnhập nhằng cho trình biên dịch và trình biên dịch sẽ gọi đúng hàm cần gọi.
    6.5. Lấy địa chỉ các hàm trùng tên
    Giả sử có 4 hàm đều có tên là tinh_max được khai báo như sau:
    int tinh_max(int a,int b,int c);//max của 3 số nguyên
    double tinh_max(double a, double b,double c);//max
    // của 3 số thực
    int tinh_max(int *a,int n);//max của dãy số nguyên
    int tinh_max(double *a, int n)// max của dãy số thục
    Vấn đề đặt ra là làm thế nào để lấy được địa chỉ của mỗi hàm. Câu trả lời như sau:
    Để lấy địa chỉ của một hàm, ta khai báo một con trỏ hàm có kiểu và bộ đối như hàm cần lấy địa chỉ. Sau đó gán tên hàm cho con trỏ hàm. Ví dụ:
    int (*f1)(int,int,int);
    f1=tinh_max;//lấy địa chỉ hàm thứ nhất
    double (*f2)(double,double,double);
    f2=tinh_max;//lấy địa chỉ hàm thứ hai
    int (*f3)(int *,int);
    f3=tinh_max;//lấy địa chỉ hàm thứ ba
    double(*f4)(double*,double);
    f4=tinh_max;//lấy địa chỉ hàm thứ tư
    6.6.Các ví dụ
    Ví dụ 1: Chương trình giải bài toán tìm max của một dãy số nguyên và max của một dãy số thực. Trong chương trình có 6 hàm. Hai hàm dung để nhập dẫy số nguyên và dãy số thực có tên chung là nhapds. Bốn hàm: tính max 2 số nguyên, tính max 2 số thực, tính max của dẫy số nguyên, tính max của dẫy số thực được đặt chung một tên là max
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<iomanip.h>
    void nhapds(int *x,int n);
    void nhapds(double *x,int n);
    int max(int x,int y);
    double max(double x,double y);
    int max(int *x,int n);
    double max(double *x,int n);
    void nhapds(int *x,int n)
       {
         for(int i=1;i<=n;i++)
           {
             cout<<”\n\n phan tu thu “<<i<<”:”;
             cin>>x[i];
           }
       }
    void nhapds(double *x,int n)
       {
         for(int i=1;i<=n;i++)
           {
             cout<<”\n\n phan tu thu “<<i<<”:”;
             cin>>x[i];
           }
       }
    int max(int x,int y)
       {
         return x>y?x:y;
       }
    double max(double x,double y)
       {
         return x>y?x:y;
       }
    int max(int *x,int n)
      {
        int s=x[1]; 
        for(int i=2;i<=n;i++)
          s=max(s,x[i]);
        return s;
      }
    double max(double *x,int n)
      {
       double s=x[1]; 
        for(int i=2;i<=n;i++)
          s=max(s,x[i]);
        return s;
      }
    void main()
       {
         int a[20],n,ni,nd,maxi;
         double x[20],maxd;
         clrscr();
         cout<<”\n\n so phan tu nguyen ni= “;
         cin>>ni;
         cout<<”\n\n nhap day so nguyen\n”;
         nhapds(a,ni);
         cout<<”\n\n so phan tu thuc nd= “;
         cin>>nd;
         cout<<”\n\n nhap day so thuc\n”;
         nhapds(x,nd);
         maxi=max(a,ni);
         maxd=max(x,nd);
         cout<<”\n\n max cua day so nguyen= “<<maxi;
         cout<<”\n\n max cua day so thuc= “<<maxd;
         getch();
       }
    Ví dụ 2:
    Chương trình thực hiện phép nhân 2 ma trận:
    D=A*B*C
    Trong đó A,B là các ma trận vuông,C là ma trận chữ nhật. Trong chương trình có 3 cặp hàm trùng tên để thực hiện 3 nhiệm vụ (nhưng trên 2 đối tượng khác nhau là ma trận vuông và ma trận chữ nhật): nhập ma trận,nhân 2 ma trận và in ma trận
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<iomanip.h>
    typedef int MT[20][20];
    void nhapmt(MT a,char *ten,int m,int n);
    void inmt(MT a,char *ten,int m,int n);
    void nhanmt(MT a,MT b,MT c,int m,int n,int p);
    void nhapmt(MT a,char *ten,int n);
    void intmt(MT a,char *ten,int n);
    void nhanmt(MT a,MT b,MT c,int n);
    void nhapmt(MT a,char *ten,int m,int n)
         {
           for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
              {
                cout<<”\n”<<ten<<”[“<<i<<”,”<<j<<”] = “;
                cin>>a[i][j];
             }
        }
    
     void nhapmt(MT a,char *ten,int n)
                     {
                       nhapmt(a,ten,n,n);
                    }
               void inmt(MT a,char *ten,int m,int n)
                    {
                      cout<<”\n\n Ma tran: “<<ten;
                      for(int i=1;i<=m;i++)
                         { 
                              
                              for (int j=1;j<=n;j++)
                              cout<<setw(6)<<a[i][j];
                         }
                   }
    void inmt(MT a,char *ten,int n)
           {
             inmt(a,ten,n,n);
           }
    void nhanmt(MT a,MT b,MT c,int m,int n,int p)
        {
          for(int i=1;i<=m;i++)
            for(int j=1;j<=p;j++)
              {
                c[i][j]=0;
                for(int k=1;k<=n;k++)
                   c[i][j]+=a[i][k]*b[k][j];
             }
        }
    void nhanmt(MT a,MT b,MT c,int n)
        {
           nhanmt(a,b,c,n,n,n);
       }
    void main()
        {
          MT a,b,c,d; //d=abc
          MT u;
          clrscr();
          nhapmt(a,”A”,2);
          nhapmt(b,”B”,2);
          nhapmt(c,”C”,2,3);
          nhanmt(a,b,u,2);
          nhanmt(u,c,d,2,2,3);
          inmt(a,”A”,2);
          inmt(a,”B”,2);
          inmt(u,”U=A*B”,2);
          inmt(c,”C”,2,3);
          inmt(d,”D=U*C”,2,3);
          getch();
    }

  3. #13
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §7. ĐỊNH NGHĨA CHỒNG CÁC TOÁN TỬ
    7.1.Các phép toán trong C và C++
    Trong C và C++ có khá nhiều phép toán để thực hiện các thao tác trên các kiểu dữ liệu chuẩn.Ví dụ các phép toán số học: + - * / áp dụng cho các kiểu dữ liệu nguyên,thực. Phép lấy phần dư % áp dụng đối với kiểu nguyên.
    7.2.Thực hiện các phép toán trên các kiểu dữ liệu chuẩn trong C
    Việc thực hiên các phép toán trên các đối tượng tự định nghĩa (như mảng,cấu trúc) là nhu cầu bắt buộc trên thực tế. Chẳng hạn cần thực hiện các phép toán số học trên số phức,trên phân số, trên đa thức,trên véctơ,trên ma trận. Để đáp ứng nhu cầu này ta dung các hàm trong C. Ví dụ sau đây là một chương trình C gồm các hàm nhập phân số,in phân số và thực hiên các phép toán cộng trừ nhân chia phân số. Chương trình sẽ nhập 5 phân số: p,q,z,u,v và tính các phân số s theo công thức:
    s=(p-q*z)/(u+v)
    Code:
    #include<conio.h>
    #include<stdio.h>
    #include<math.h>
    typedef struct
       {
         int a,b;
       } PS;
    void nhap(PS *p);
    void in(PS p);
    int uscln(int x,int y);
    PS rutgon(PS p);
    PS cong(PS p1,PS p2);
    PS tru(PS p1,PS p2);
    PS nhan(PS p1,PS p2);
    PS chia(PS p1,PS p2);
    void nhap(PS *p)
       {
         int t,m;
         printf(“\n Tu va mau: “);
         scanf(“%d%d”,&t,&m);
         p->a=t;p->b=m;
      }
    void in(PS p)
       {
         printf(“%d/%d”,p.a,p.b);
       }
    int uscln(int x,int y)
        {
          x=abs(x);y=abs(y);
          if(x*y==0) return 1;
          while(x!=y)
              if(x>y) x-=y;
              else y-=x;
           return x;
         }
    PS rutgon(PS p)
         {
           PS q;
           int x;
           x=uscln(p.a,p.b);
           q.a=p.a/x;
           q.b=p.b/x;
            return q;
         }
    PS cong(PS p1,PS p2)
         {
           PS q;
            q.a=p1.a*p2.b+p1.b*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
         }
    PS tru(PS p1,PS p2)
         {
           PS q;
            q.a=p1.a*p2.b-p1.b*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
         }
    PS nhan(PS p1,PS p2)
          {
            PS q;
            q.a=p1.a*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
          }
    PS chia(PS p1,PS p2)
          {
            PS q;
            q.a=p1.a*p2.b;
            q.b=p1.b*p2.a;
            return rutgon(q);
          }
    void main()
          {
            PS p,q,z,u,v;
            PS tu,mau,s;
            printf(“\n\n mhap phan so p: “);nhap(&p);
            printf(“\n\n mhap phan so q: “);nhap(&q);
            printf(“\n\n mhap phan so z: “);nhap(&z);
            printf(“\n\n mhap phan so u: “);nhap(&u);
            printf(“\n\n mhap phan so v: “);nhap(&v);
            tu=nhan(q,z);
            tu=tru(p,tu);
            mau=cong(u,v);
            s=chia(tu,mau);
            printf(“\n\n phan so s= “);in(s);
            getch();
           }
    Nhận xét: Việc sử dụng các hàm để thực hiện các phép tính toán tỏ ra không được tự nhiên và dài dòng. Ví dụ để thực hiện một công thức
    s=(p-q*z)/(u+v)
    phải dung 2 biến trung gian và 4 lời gọi hàm. Câu hỏi đặt ra là có cách nào để chỉ cần viết đúng 1 công thức toán học mà vẫn nhận được kết quả như ý muốn hay không?
    Trong C++ có thể đáp ứng được mong muốn này bằng cách sử dụng các phép toán chuẩn của nó cho các kiểu dữ liệu tự định nghĩa(mảng ,cấu trúc…). Nói cách khác C++ cho phép dung các phép toán để định nghĩa các hàm mà ta thường gọi là định nghĩa chồng toán tử( hay còn gọi là sự tải bội các toán tử).
    7.3. Cách định nghĩa chồng các toán tử
    7.3.1. Tên hàm toán tử: gồm từ khoá operator và tên phép toán,ví dụ: (định nghĩa chồng phép cộng)
    operator- (định nghĩa chồng phép trừ)
    7.3.2. Các đối của hàm toán tử
    a. Với các phép toán có 2 toán hạng, thì hàm toán tử chỉ cần có 2 đối, đối thứ nhất ứng với toán hạng thứ nhất, đối thứ hai ứng với toán hạng thứ hai. Do vậy,với các phép toán không giao hoán (như phép trừ) thì thứ tự đối là rất quan trọng
    ví dụ: các hàm toán tử cộng,trừ phân số được khai báo như sau:
    struct PS
    {
    int a; //Tử số
    int b; // Mẫu số
    }
    PS operator+(PS s1,PS s2); //p1+p2
    PS operator-(PS s1,PS s2); //p1-p2
    PS operator*(PS s1,PS s2); // p1*p2
    PS operator/(PS s1,PS s2);// p1/p2
    b. Với các phép toán có một toán hạng, thì hàm toán tử có một đối. Ví dụ hàm toán tử đổi dấu ma trận (đổi dấu tất cả các phần tử của ma trận) được khai báo như sau:
    struct MT
    {
    double a[20][20]; //mảng chứa các phần tử ma trận
    int m; //số hàng ma trận
    int n; số cột ma trận
    };
    MT operator-(MT x);
    7.3.3. Thân của hàm toán tử: Viết như thân của hàm bình thường. Ví dụ hàm đổi dấu ma trận có thể được định nghĩa như sau:
    struct MT
    {
    double a[20][20];// mảng chứa các ptử ma trận
    int a; // số hang của ma trận
    int b; // số cột của ma trận
    };
    MT operator-(MT x)
    {
    MT y;
    for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
    y[i] [j]=-x[i][j];
    return y;
    }
    7.4. Cách dung hàm toán tử
    Có 2 cách dung:
    Cách 1: dung như một hàm thong thường bằng cách viết lời gọi
    Ví dụ:
    PS p,u,v,q;
    u=operator+(p,q); //u=p+q
    v=operator-(p,q); //v=p-q
    cách 2: dung như phép toán trong C++
    ví dụ:
    PS p,q,u,v;
    u=p+q;
    v=p-q;
    Chú ý: Khi dùng các hàm toán tử như phép toán của C++ ta có thể kết hợp nhiều phép toán để viết các công thức phức tạp. Cũng cho phép dung dấu ngoặc tròn để quy định thứ tự thực hiện các phép tính. Thứ tự ưu tiên của các phép tính vẫn tuân theo qui tắc ban đầu của C++. Chẳng hạn các phép toán * và/ có thứ tự ưu tiên cao hơn so với phép + và –
    Ví dụ:
    PS p,q,u,v,s1,s2;
    s1=p*q-u/v;
    s2=(p-q)/u+v);

  4. #14
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §8. CÁC VÍ DỤ VỀ ĐỊNH NGHĨA CHỒNG TOÁN TỬ
    Ví dụ 1: trong ví dụ này ngoài việc sử dụng các hàm toán tử để thực hiện 4 phép tính trên phân số,còn định nghĩa chồng các phép toán << và >> để xuất nhập phân số (xem chi tiết trong chương 7).
    Hàm operator<< có 2 đối kiểu ostream& và PS (phân số). Hàm trả về giá trị kiểu ostream&. Hàm được khai báo như sau:
    ostream& operator<<(ostream& os, PS p);
    tương tự hàm operator>> được khai báo như sau:
    istream& operator>>(istream& is,PS &p);
    dưới đây sẽ chỉ ra cách xây dựng và sử dụnh các hàm toán tử. Chúng ta cũng sẽ thấy việc sử dụng các hàm toán tử rất tự nhiên , ngắn gọn và tiện lợi
    chương trình dưới đây có nội dung như trong chương trình §6.2, nhưng thay các hàm bằng các toán tử:
    Code:
    #include<conio.h>
    #include<iostram.h>
    #include<math.h>
    typedef struct
          {
             int a,b;
          } PS;
    ostream& operator<<(ostream& os,PS p);
    istream& operator>>(iostream& is,PS p);
    int uscln(int x,int y);
    PS rutgon(PS p);
    PS operator+(PS p1,PS p2);
    PS operator-(PS p1,PS p2);
    PS operator/(PS p1,PS p2);
    PS operator*(PS p1,PS p2);
    ostream& operator<<(ostream& os,PS p)
       {
         os<<p.a<<”/”<<p.b;
         return os;
       }
    istream& operator>>(istream& is,PS p)
        {
          cout>>”\n nhap tu va mau”;
          is>>p.a>>p.b;
          return is;
        }
      int uscln(int x,int y)
        {
          x=abs(x);y=abs(y);
          if(x*y==0) return 1;
          while(x!=y)
              if(x>y) x-=y;
              else y-=x;
           return x;
         }
    PS rutgon(PS p)
         {
           PS q;
           int x;
           x=uscln(p.a,p.b);
           q.a=p.a/x;
           q.b=p.b/x;
            return q;
         }
    PS operator+(PS p1,PS p2)
         {
           PS q;
            q.a=p1.a*p2.b+p1.b*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
         }
    PS operator-(PS p1,PS p2)
         {
           PS q;
            q.a=p1.a*p2.b-p1.b*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
         }
    PS operator*(PS p1,PS p2)
          {
            PS q;
            q.a=p1.a*p2.a;
            q.b=p1.b*p2.b;
            return rutgon(q);
          }
    PS operator/(PS p1,PS p2)
          {
            PS q;
            q.a=p1.a*p2.b;
            q.b=p1.b*p2.a;
            return rutgon(q);
          }
    void main()
          {
            PS p,q,z,u,v;
            PS s;
            cout<<”\n nhap cac phan so p,q,z,u,v: \n”;
            cin>>p>>q>>z>>u>>v;
            s=(p-q*z)/(u+v);
            cout<<”\n phan so s= “<<s;
            getch();
          }
    Ví dụ 2: Chương trình đưa vào các hàm toán tử
    operator- có một đối dung để đảo dấu một đa thức
    operator+ có 2 đối dùng để cộng 2 đa thức
    operator- có 2 đối dùng để trừ 2 đa thức
    operator* có 2 đối dùng để nhân 2 đa thức
    operator^ có 2 đối dùng để tính giá đa thức tại x
    operator<< có 2 đối dùng để in đa thức
    operator>> có 2 đối dùng để nhập đa thức
    chương trình sẽ nhập 4 đa thức: p,q,r,s,sau đó tính đa thức: f= -(p+q)*(r-s)
    cuối cùng tính giá trị f(x), với x là một số thực được nhập từ bàn phím
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<math.h>
    struct DT
       {
         double a[20]; //mang chua cac he so cua da thuc
         int n; // bac cua da thuc
      };
    ostream& operator<<(ostream& os, DT d);
    istream& operator>>(istream& is, DT &d);
    DT operator-(const DT& d);
    DT operator+(DT d1, DT d2);
    DT operator-(DT d1, DT d2);
    DT operator*(DT d1, DT d2);
    double operator^(DT d,double x); //tinh gia tri da thuc
    ostream& operator<<(ostream& os, DT d)
       {
         os<<” CAC HE SO (tu Ao): “;
         for(int i=0;i<=d.n;i++)
            os<<d.a[i]<<” “;
         return os;
       }
    istream& operator>>(istream& is,DT &d)
        {
          cout<<” bac cua da thuc: “;
          cin>>d.n;
          cout<<” Nhap cac he so cua da thuc: \n”;
          for(int i=0;i<=d.n;i++)
            {
              is>>d.a[i];
            }
          return is;
       }
    DT operator-(const DT& d)
       {
         DT p;
         p.n=d.n;
         for(int i=0;i<=d.n;i++)
           p.a[i]=-d.a[i];
         return p;
       }
    DT operator+(DT d1,DT d2)
        {
           DT d;
           int k,i;
           k=d1.n>d2.n?d1.n:d2.n;
           for(i=0;i<=k;i++)
              if(i<=d1.n&&i<=d2.n)
                d.a[i]=d1.a[i]+d2.a[i];
              else if(i<=d1.n)
                d.a[i]=d1.a[i];
              else
                d.a[i]=d2.a[i];
           i=k;
           while(i>0&&d.a[i]==0.0) –I;
            d.n=i;
            return d;
         }
    DT operator-(DT d1, DT d2)
         {
           return(d1+(-d2));
         }
    DT operator*(DT d1,DT d2)
         {
            DT d;
            int k,i,j;
            k=d.n=d1.n+d2.n;
            for(i=0;i<=k;i++)
               d.a[i]=0;
            for(i=0;i<=d1.n;i++)
            for(j=0;j<=d2.n;j++)
                d.a[i+j]+=d1.a[i]*d2.a[j];
             return d;
         }
    double operator^(DT d,double x)
          {
            double s=0.0, t=1.0;
            for(int i=0;i<=d;i++)
              {
                s+=d.a[i]*t;
                t*=x;
             }
            return s;
          }
    void main()
          {
             DT p,q,r,s,f;
             double x,g;  
             clrscr();
             cout<<”\n\n nhap da thuc P: “;cin>>p;
             cout<<”\n\n nhap da thuc Q: “;cin>>q;
             cout<<”\n\n nhap da thuc R: “;cin>>r;
             cout<<”\n\n nhap da thuc S: “;cin>>s;
             cout<<”\n\n nhap so thuc x: “;cin>>x;
             f=-(p+q)*(r-s);
             g=f^x;
             cout<<”\n\n da thuc f”<<f;
             cout<<”\n\n x= “<<x;
             cout<<”\n\n f(x)= “;<<g;
             getch();
          }

  5. #15
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §9.CÁC BÀI TOÁN VỀ MA TRẬN VÀ VECTƠ
    trong mục này sẽ xét các ma trận thực vuông cấp n và các vectơ thực cấp n. Chúng được biểu diễn thông qua các kiểu cấu trúc MT và VT:
    struct MT
    {
    double a[20][20];//mang a chua cac phtu ma tran
    int n; // cap ma tran
    };
    struct VT
    {
    double b[20]; //mang chua cac phan tu cua vecto
    int n; //cap vecto
    };
    để xử lí ma trận và vectơ chúng ta xây dựng 9 hàm toán tử:
    ostream& operator<<(ostream& os,const MT& x);//in ma tran
    ostream& operator<<(ostream& os,const VT& v);//in vecto
    istream& operator>>(istream& is,MT& x);//nhap ma tran
    istream& operator>>(istream& is,VT& v);//nhap vecto
    MT operator+(const MT& x1,const MT& x2);//cong 2 ma tran
    MT operator-(const MT& x1,const MT& x2);//tru 2 ma tran
    MT operator*(const MT& x1,const MT& x2);//nhan 2 ma tran
    MT operator*(const MT& x1,const VT& v);//nhan ma tran vecto
    MT operator!(MT x);
    thuật toán cho 8 hàm toán tử đầu tương đối quen thuộc không có gì phải bàn. Để nghịch đảo ma trận có nhiều cách, ở đây chúng ta dùng phương pháp Jordance như sau. Giả sử cần nghịch đảo ma trận x cấp n. Ta dùng thêm ma trận đơn vị y. Sau đó thực hiện đồng thời các phép tính trên cả x và y sao cho x trở thành đơn vị. Kết quả là y chính là nghịch đảo của x. Thuật toán được tiến hành trên n bước. Nội dung của bước k (k=1,...,n) như sau:
    Tìm chỉ số r (k<=r<=n) sao cho
    abs(x[r,k])=max{abs(x[i,k]) với i=k,...,n}
    Nếu abs(x[r,k])=0 thì ma trận không có nghịch đảo và thuật toán kết thúc giữa chừng.
    Hoán vị hàng k với hàng r trong cả 2 ma trận x và y.
    Chia hàng k của cả x và y cho tg=x[k,k] (mục đích làm cho x[k,k]=1)
    biến đổi để cột k của hàng x trở thành vectơ đơn vị bằng cách làm cho các phần tử x[i,k]=0 (với i khác k). Muốn vậy ta thực hiện các phép tính sau trên cả x và y:
    (hàng i)= (hàng i)-x[i,k]*(hàng k), với mọi i khác k
    nội dung chương trình là nhập 4 ma trận X,Y,R,S và vectơ u. Sau đó tính vectơ v theo công thức:
    v=((X+Y)*(R-S)^-1u
    Như sẽ thấy trong hàm main() dưới đây, nhờ các hàm toán tử mà câu lệnh tính v được viết gần giống như công thức toán học nêu trên.
    /* chương trình */

    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<iomanip.h>
    #include<math.h>
    struct MT
              {
                double a[20][20];//mang a chua cac phtu ma tran
                int n; // cap ma tran
              };
           struct VT
              {
                double b[20]; //mang chua cac phan tu cua vecto
                int n; //cap vecto
              };
    ostream& operator<<(ostream& os,const MT& x);      ostream& operator<<(ostream& os,const VT& v);
    istream& operator>>(istream& is,MT& x);       
    istream& operator>>(istream& is,VT& v);
          MT operator+(const MT& x1,const MT& x2);
          MT operator-(const MT& x1,const MT& x2);
          MT operator*(const MT& x1,const MT& x2);       
    MT operator*(const MT& x1,const VT& v);
    MT operator!(MT x);// tinh ma tran nghich dao
    ostream& operator<<(ostream& os,const MT& x)
        {
          os<<setprecision(2)<<setiosflags(ios::showpoint);
          for(int i=1;i<=x.n;i++)
            {
              os<<”\n\n”;
              for(int j=1;j<=x.n;j++)
              os<<setw(6)<<x.a[i][j];
            }
          os<<”\n”;
        }
    ostream& operator<<(ostream& os,const VT& v)
         {
           os<<setprecision(2)<<setiosflags(ios::showpoint);
           for(int i=1;i<=v.n;i++)
             os<<setw(6)<<v.b[i];
           os<<”\n”;
           return os;
         }
    istream& operator>>(istream& is,MT& x)
         {
           cout<<” Cap ma tran:”;
           is>>x.n;
           cout<<” \n nhap cac phan tu: \n”;
           for(int i=1;i<=x.n;i++)
             for(int j=1;j<=x.n;j++)
              {
                cout<<” PT hang”<<i<< “ cot ”<<j<<”=”;
                is>>x.a[i][j];
             }
           return is;
        }
    istream& operator>>(istream& is,VT& v)
         {
           cout<<” cap vecto: “;
           is>>v.n;
           cout<<”\n nhap cac phan tu: \n”;
           for(int i=1;i<=v.n;i++)
             {
               cout<<”\n phan tu thu “<<i<<”=”;
               is>>v.b[i];
             }
           return is;
        }
    MT operator+(const MT& x1,const MT& x2)
          {
            if(x1.n!x2.n)
                {
                  cout<<”\n Khong thuc hien duoc phep cong vi hai ma tran khong cung cap”;
                  getch();
                  return x1;
               }
            else
                {
                  MT x;
                  int i,j,n;
                  n=x.n=x1.n;
                  for(i=1;i<=n;i++)
                    for(j=1;j<=n;j++)
                       x.a[i][j]=x1.a[i][j]+x2.a[i][j];
                  return x;
                }
             }
    MT operator-(const MT& x1,const MT& x2)
          {
            if(x1.n!x2.n)
                {
                  cout<<”\n Khong thuc hien duoc phep cong vi hai ma tran khong cung cap”;
                  getch();
                  return x1;
               }
            else
                {
                  MT x;
                  int i,j,n;
                  n=x.n=x1.n;
                  for(i=1;i<=n;i++)
                    for(j=1;j<=n;j++)
                       x.a[i][j]=x1.a[i][j]-2.a[i][j];
                  return x;
                }
             }
    MT operator*(const MT& x1,const MT& x2)
          {
            if(x1.n!x2.n)
                {
                  cout<<”\n Khong thuc hien duoc phep cong vi hai ma tran khong cung cap”;
                  getch();
                  return x1;
               }
            else
                {
                  MT x;
                  int i,j,n,k;
                  n=x.n=x1.n;
                  for(i=1;i<=n;i++)
                    for(j=1;j<=n;j++)
                       {
                        x.a[i][j]=0.0;
                        for(k=1;k<=n;k++)
                          x.a[i][j]+=x1.a[i][k]*x2.a[i][k];
                       }
                  return x;
                }
             }
        VT operator*(const MT& x,const VT& v)
              {
                if(x.n!v.n)
                   {
                      cout<<”\n cap ma tran khac cap vecto,phep nhan vo nghia”;
                      getch();
                      return v;
                   }
                else
                   {
                     VT u;int n;
                     n=u.n=v.n;
                     for(int i=1;i<=n;i++)
                         {
                           u.b[i]=0;
                           for(int j=1;j<=n;j++)
                             u.b[i]+=x.a[i][j]*v.b[j];
                         }
                       return u;
                   }
                }
        MT operator!(MT x)
            {
              MT y;
              int i,j,k,r,n;
              double tg;
              n=y.n=x.n;
              for(i=1;i<=n;i++)
               for(j=1;j<=n;j++)
                 if(i==j) y.a[i][j]=0;
              for(k=1;k<=n;k++)
                {
                  r=k; 
                  for(i=k+1;i<=n;i++)
                    if(abs(x.a[i][k])>abs(x.a[r][k])) r=i;
                   if(abs(x.a[r][k])<1.0E-8)
                      {
                        cout<<”\n ma tra suy bien,khong co nghich dao”;
                        getch(); return x;
                      }
                   //hoan vi hang r va hang k
                  for(j=1;j<=n;j++)
                    {
                      tg=x.a[k][j];
                      x.a[k][j]=x.a[r][j];
                      x.a[r][j]=tg;
                      tg=y.a[k][j];
                      y.a[k][j]=y.a[r][j];
                      y.a[r][j]=tg;
                    }
                  //chia hang k cho a[k,k]
                  tg=x.a[k][k];
                  for(j=1;j<=n;j++)
                    {
                      x.a[k][j]/=tg;
                      y.a[k][j]/=tg;
                   }
                  /* khu cot k: lam cho a[i,k]=0 voi i!=k */
                  for(int i=1;i<=n;i++)
                    if(i!=k)
                      {
                        tg=x.a[i][k];
                        for(j=1;j<=n;j++)
                          { 
                            x.a[i][j]-=tg*x.a[k][j];
                            y.a[i][j]-=tg*y.a[k][j];
                          }
                       }
                 }
               return y;
             }
        void main()
             {
               MT x,y,r,s; 
               VT u,v;
               clrscr();
               cout<<”\n Nhap ma tran X: “;cin>>x;
               cout<<”\n Nhap ma tran Y: “;cin>>y;
               cout<<”\n Nhap ma tran R: “;cin>>r;
               cout<<”\n Nhap ma tran S: “;cin>>s;
               cout<<”\n Nhap vecto u”;cin>>u;
               v=!((x+y)*(r-s))*u;
               cout<<”\n Vecto v=xu”<<v;
               getch();
            }

  6. #16
    Ngày gia nhập
    12 2006
    Bài viết
    72

    Mặc định Lập trình C++ | Tài liệu lập trình C++ của Phạm Văn Ất

    Vậy là mình đã post xong những bài cơ bản trước khi bước vào phần quan trọng nhất của cuốn giáo trình nói về lập trình hướng đối tượng. Và hôm nay mình xin bắt đầu bài đầu tiên về lập trình hướng đối tượng.
    Chương 3: KHÁI NIỆM VỀ LỚP
    §1. Định nghĩa lớp
    1. Lớp được khai báo theo mẫu:
    Class Tên_lớp
    {
    //Khai báo các thành phần dữ liệu(thuộc tính)
    //Khai báo các phương thức
    }
    //Định nghĩa xây dựng các phương thức
    Chú ý:
    Thuộc tính của lớp có thể là các biến,mảng,con trỏ có kiểu chuẩn(int,float,char,char*,long…) hoặc kiểu ngoài chuẩn đã định nghĩa trước(cấu trúc,hợp,lớp,…). Thuộc tính của lớp không thể có kiểu của chính lớp đó, nhưng có thể có kiểu của con trỏ lớp này,ví dụ:
    class A
    {
    A x;// không cho phép,vì x có kiểu lớp A
    A *p;// cho phép vì p là con trỏ kiểu lớp A
    };
    2.khi khai báo các thành phần của lớp(thuộc tính và phương thức) có thể dung các từ khóa private và public để qui định phạm vi sử dụng của các thành phần.Nếu không qui định cụ thể(không dung các từ khóa private và public) thì C++ hiểu đó là private.
    Các thành phần của private chỉ được sử dụng bên trong lớp(trong thân các phương thức của lớp).Các hàm không phải là phương thức của lớp không được phép sử dụng các thành phần này.
    Các thành phần của private được phép sử dụng cả bên trong và bên ngoài lớp
    3.Các thành phần dữ liệu thường(nhưng không bắt buộc) khai báo là private để đảm bảo tính dấu kín, bảo vệ sự an toàn dữ liệu của lớp, không cho phép các hàm bên ngoài thâm nhập vào dữ liệu của lớp.
    4.Các phương thức thường khai báo là public để chúng có thể được gọi tới(sử dụng) từ các hàm khác trong chương trình.
    5.Các phương thức có thể được xây dựng từ bên ngoài hay bên trong định nghĩa lớp. Thông thường các phương thức ngắn thường được viết bên trong lớp còn các phương thức dài thường được viết bên ngoài định nghĩa lớp.
    6.Trong thân phương thức của một lớp giả sử lớp A có thể sử dụng :
    +) Các thuộc tính của lớp A
    +) Các phương thức của lớp A
    +) Các hàm tự lập trong chương trình. Vì phạm vi sử dụng của hàm là toàn bộ chương trình
    7.Giá trị trả về của phương thức có thể có kiểu bất kì(chuẩn hoặc ngoài chuẩn)
    Ví dụ sau sẽ minh họa các điều đã nói ở trên. Chúng ta sẽ xây dựng một lớp để mô tả và sử lí các điểm trên màn hình đồ họa.Lớp được đặt tên là DIEM
    +)Các thuộc tính của lớp bao gồm:
    int x,y;// hoành độ và tung độ
    int m; mầu
    +)Các phương thức:
    Nhập dữ liệu một điểm
    Hiển thị một điểm
    Ẩn một điểm
    Lớp DIEM được xây dựng như sau:
    class DIEM
    {
    private:
    int x,y,m;
    public:
    void nhap();
    void hien();
    void an()
    {
    putpixel(x,y,getbkcolor());
    }
    };
    void DIEM::nhap()
    {
    cout<<”\n Nhap hoanh do va tung do: “;cin>>x>>y;
    cout<<”\n nhap ma mau cua diem: “;cin>>m;
    }
    void DIEM::hien()
    {
    int mau_ht;
    mau_ht=getcolor();
    putpixel(x,y,m);
    setcolor(mau_ht);
    }
    Qua ví dụ trên ta rút ra một số điểu cần nhớ sau:
    +) Trong cả 3 phương thức dù viết trong hay viết ngoài định nghĩa lớp đều được phép truy nhập đến các thuộc tính x,y và m của lớp
    +) Các phương thức viết bên trong định nghĩa lớp được viết như một hàm thông thường
    +) Khi xây dựng các phương thức bên ngoài lớp, cần dùng them tên lớp vầ toán tử phạm vi:: đặt ngay trước tên phương thức để quy định rõ đây là phương thức của lớp nào.
    Vì đây là phần kiến thức khá trừu tượng đối với các bạn mới bước vào lập trình hướng đối tượng nên mình sẽ post mỗi lần một ít để các bạn có thể làm quen một cách dễ dàng hơn, có gì xin các bạn chỉ giáo.

  7. #17
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §2 Biến và mảng đối tượng
    Như đã nói ở trên một lớp sau khi định nghĩa có thể xem như một kiểu đối tượng và có thể dùng để khai báo các biến, mảng đối tượng. Cách khai báo biến, mảng đối tượng cũng giống như khai báo biến, mảng khác( int,float,double…), theo mẫu sau:
    Tên_lớp danh sách đối;
    Tên_lớp danh sách mảng;
    Ví dụ về lớp điểm ở bài 1 có thể dùng để khai báo các biến, mảng DIEM như sau:
    DIEM d1,d2,d3;// Khai báo 3 biến đối tượng d1,d2,d3
    DIEM d[20]; //Khai báo mảng đối tượng d có 20 phần tử
    Mỗi đối tượng sau khi khai báo sẽ được cấp phát một vùng nhớ riêng để chứa các thuộc tính của chúng. Chú ý sẽ không có vùng nhớ riêng để chứa các phương thức cho mỗi đối tượng. Các phương thức sẽ được sử dụng chung cho tất cả các đối tượng cùng lớp. Như vậy về bộ nhớ được cung cấp thì đối tượng giống cấu trúc. Trong trường hợp này:
    sizeof(d1)=sizeof(d2)=sizeof(d3)=3*sizeof(int)==6
    sizeof(d)=20*6=120
    chú thích: mỗi biến đối tượng có 3 thuộc tính kiểu int nên bộ nhớ cung cấp cho mỗi biến đối tượng==bộ nhớ cung cấp cho 3 biến đó.
    Thuộc tính của đối tượng
    Trong ví dụ trên mỗi đối tượng d1,d2,d3 và mỗi phần tử d[i] đều có 3 thuộc tính x,y,m. Chú ý là mỗi thuộc tính đều thuộc về một đối tượng vì vậy không thể viết tên thuộc tính một cách riêng rẽ mà bao giờ cũng phải có tên đối tượng đi kèm giống như trong cách viết cấu trúc của C hay bản ghi của Pascal. Nói chung cách viết thuộc tính của đối tượng như sau:
    Tên_đối_tượng.Tên_thuộc_tính
    Với các đối tượng d1,d2,d3 và mảng d,có thể viết như sau:
    d1.x // thuộc tính x của đối tượng d1
    d2.x //thuộc tính x của đối tượng d2
    d[2].y //thuộc tính y của phần tử d[2]
    ….
    Sử dụng các phương thức
    Cũng giống như hàm, mỗi phương thức được sử dụng thông qua lời gọi. Tuy nhiên trong lời gọi phương thức bao giờ cũng phải có tên đối tượng để chỉ rõ phương trức thực hiện trên thuộc tính của đối tượng nào. Ví dụ lời gọi:
    d1.nhap();
    sẽ thực hiện lệnh nhập số liệu vào các thành phần d1.x,d1.y,d1.m;
    Now ! chúng ta xây dựng một chương trình để minh họa các điều đã nói ở 2 bài trên. Sử dụng lớp điểm để nhập 3 điểm rồi cho ẩn các điểm vừa nhập. Trong chương trình
    đưa vào hàm kd_do_hoa() dùng để khởi động hệ đồ họa
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<graphics.h>
    class DIEM
      {
       private:
         int x,y,m;
      public:
         void nhap();
         void hien();
         void an()
           {
             putpixel(x,y,getbkcolor());
           }
        };
    void DIEM::nhap()
      {
        cout<<”\n Nhap hoanh do va tung do: “;cin>>x>>y;
        cout<<”\n nhap ma mau cua diem: “;cin>>m;
     }
    void DIEM::hien()
       {
         int mau_ht;
         mau_ht=getcolor();
         putpixel(x,y,m);
         setcolor(mau_ht);
      }
      void kd_do_hoa()
       {
         int mh,mode;
         mh=mode=0;
         initgraph(&mh,&mode,””);
       }
     void main()
      {
        DIEM d1,d2,d3;
        d1.nhap();d2.nhap();d3.nhap();
        kd_do_hoa();
        setbkcolor(BLACK);d1.hien();d2.hien();d3.hien();
        getch();
        d1.an();
        d2.an() ;d3.an();
        getch();
        closegraph();
    }

  8. #18
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §3. Con trỏ đối tượng
    Con trỏ đối tượng dùng để chứa địa chỉ của biến, mảng đối tượng. Nó được khai báo như sau:
    Tên_lớp *con trỏ;
    Ví dụ: dùng lớp DIEM để khai báo:
    DIEM *p1,*p2,*p3;// khai báo 3 con trỏ p1,p2,p3
    DIEM d1,d2;// khai báo 2 đối tượng d1,d2
    DIEM d[20];//khai báo mảng đối tượng
    Và có thể thực hiện các câu lệnh:
    p1=&d2; //p1 chứa địa chỉ của d2, hay p1 trỏ tới d2
    p2=d; //p2 trỏ tới đầu mảng d
    p3=new DIEM; //Tạo một đối tượng và chứa địa chỉ của nó
    // vào p3
    Để sử dụng thuộc tính của một đối tượng thông qua con trỏ, ta viết như sau:
    Tên_con_trỏ->Tên thuộc tính
    Chú ý: nếu con trỏ chứa địa chỉ đầu của mảng có thể dùng con trỏ như tên mảng.
    Như vậy sau khi thực hiện các câu lệnh trên thì:
    p1->x và d2.x là như nhau
    p2[i].y và d[i].y là như nhau
    Tóm lại ta có qui tắc sau gọi là qui tắc sử dụng thuộc tính
    Để sử dụng 1 thuộc tính của 1 đối tượng ta phải dùng phép “.” Hoặc phép “->” .Trong chương trình không cho phép sử dụng thuộc tính 1 cách đơn độc mà phải đi kèm theo tên đối tượng hoặc tên con trỏ theo mẫu sau:
    Tên đối tượng.tên thuộc tính
    Tên con trỏ->tên thuộc tính
    Tên mảng đối tượng[chỉ số].tên thuộc tính
    Tên con trỏ[chỉ số].tên thuộc tính
    Chương trình sau sử dụng một lớp DIEM để nhập 1 dãy điểm, hiển thị và ẩn các điểm vừa nhập.Chương trình dùng con trỏ kiểu DIEM và dùng toán tử new để tạo dãy đối tượng.
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<graphics.h>
    class DIEM
      {
        private:
         int x,y,m;
        public:
         void nhapsl()
           {
             cout<<”\n nhap chi so cho diem: “;
             cin>>x>>y;
             cout<<”\n nhap ma mau cua diem: “;
             cin>>m;
           }
        void an()
           {
             putpixel(x,y,getbkcolor());
           }
        void hien()
           {
             int mau_ht;
             mau_ht=getcolor();
             putpixel(x,y,m);
             setcolor(mau_ht);
           }
        };
    void khoidongdohoa()
       {
         int gr=0,mode=0;
         initgraph(&mh,&mode,”..\\BGI”);
         
      }
    void main()
      {
        DIEM *p;
        int i,n; 
        cout<<”\n nhap so diem: “;
        cin>>n;
        p=new DIEM[n+1];
        for(i=1;i<=n;i++)
          p[i].nhapsl();
        khoidongdohoa();
       for(i=1;i<=n;i++)
          p[i].hien();
       getch();
       for(i=1;i<=n;i++)
          p[i].an();
       getch();
       closegraph();
    }

  9. #19
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §4. Đối của phương thức, con trỏ this
    4.1. Con trỏ this là đối thứ nhất của phương thức
    Chúng ta hãy xem phương thức nhapsl của lớp DIEM
    void nhapsl()
    {
    cout<<”\n nhap chi so cho diem: “;
    cin>>x>>y;
    cout<<”\n nhap ma mau cua diem: “;
    cin>>m;
    }
    Rõ rang trong phương thức này ta sử dụng tên các thuộc tính x,y,m một cách đơn độc. Điều này có vẻ mâu thuẫn với quy tắc sử dụng thuộc tính trong mục trước. Song cụ thể như sau:
    C++ sử dụng con trỏ đặc biệt this trong các phương thức.Các thuộc tính trong các phương thức được hiểu là thuộc một đối tượng do con trỏ this trỏ tới. Như vậy phương thức nhapsl có thể viết một cách tường ming như sau:
    void DIEM::nhapsl()
    {
    cout<<”\n nhap hoanh do va tung do cua diem: “;
    cin>>this->x>>this->y;
    cout<<”\n nhap ma mau cua dien: “;
    cin>>this->m;
    }
    Từ góc độ hàm số có thể kết luận rằng: Phương thức bao giờ cũng có ít nhất một đối là con trỏ this và nó luôn luôn là đối đầu tiên của phương thức.
    4.2. Tham số ứng với con trỏ this
    Xét một lời gọi hàm tới phương thức nhapsl();
    DIEM dl;
    dl.nhapsl();
    trong trường hợp này tham số truyền cho con trỏ this chính là địa chỉ của dl:
    this=&dl;
    do đó this->x chính là dl.x;
    this->y chính là dl.y;
    this->m chính là dl.m;
    như vậy câu lệnh dl.nhapsl(); sẽ nhập số liệu cho các thuộc tính của đối tượng dl. Từ đó có thể rút ra kết luận sau:
    Tham số truyền cho đối con trỏ this chính là địa chỉ của đối tượng đi kèm với phương thức trong lời gọi phương thức.
    4.3.Các đối khác của phương thức
    Ngoài đối đặc biệt this(đối này không xuất hiện một cách tường minh), phương thức còn có các đối khác được khai báo như các hàm. Đối của phương thức có thể có kiểu bất kì(chuẩn hoặc ngoài chuẩn)
    Ví dụ: để xây dựng phương thức vẽ đường thẳng đi qua 2 điểm ta cần đưa vào 3 đối. Hai đối là 2 biến kiểu DIEM, đối thứ 3 là kiểu nguyên xác định mã màu. Vì đã có đối ngầm là this là đối thứ nhất, nên chỉ cần khai báo them 2 đối. Phương thức có thể viết như sau:
    void DIEM::doanthang(DIEM d2,int mau)
    {
    int mau_ht;
    mau_ht=getcolor();
    setcolor(mau);
    line(this->x,this->y,d2.x,d2.y);
    setcolor(mau_ht);
    }
    Chương trình sau minh họa các phương thức có nhiều đối. Ta vẫn dùng lớp DIEM nhưng có 1 số thay đổi:
    + Bỏ thuộc tính m(mầu)
    + Bỏ các phương thức hien và an
    + Đưa vào các phương thức mới:
    ve_doan_thang (Vẽ đoạn thẳng đi qua 2 điểm)
    ve_tam_giac (Vẽ tam giác đi qua 3 điểm)
    do_dai (Tính độ dài của đoạn thẳng đi qua 2 điểm)
    chu_vi( Tính chu vi tam giác qua 3 điểm)
    Chương trình còn minh họa:
    +. Việc phương thức này sử dụng phương thức khác (phương thức ve_tam_giac sử dụng phương thức ve_doan_thang, phương thức chu_vi sử dụng phương thức do_dai)
    +. Sử dụng con trỏ this trong thân các phương thức ve_tam_giac và chu_vi
    Nội dung của chương trình là nhập 3 điểm, vẽ tam giác có đỉnh là 3 điểm vừa nhập sau đó tính chu vi tam giác.
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<graphics.h>
    #include<math.h>
    #include<stdio.h>
    class DIEM
     {
       private:
         int x,y;
      public:
        void nhapsl();
        void ve_doan_thang(DIEM d2,int mau);
        void ve_tam_giac(DIEM d2,DIEM,d3,int mau);
        double do_dai(DIEM d2)
         {
           DIEM d1=*this;
           return sqrt( pow(d1.x-d2.x,2)+pow(d1.y-d2.y,2));
         }
       double chu_vi(DIEM d2,DIEM d3);
     };
    void DIEM::nhapsl()
      {
        cout<<”\n nhap hoanh do va tung do: “;
        cin>>x>>y;
      }
    void khoidongdohoa()
       {
         int gr=0,mode=0;
         initgraph(&mh,&mode,”..\\BGI”);
         
      }
            void DIEM::ve_doan_thang(DIEM d2,int mau)
               {
                 setcolor(mau);
                 line(this->x,this->y,d2.x,d2.y);
               }
           void DIEM::ve_tam_giac(DIEM d2,DIEM d3,int mau)
               {
                 (*this).doanthang(d2,mau);
                 d2.doanthang(d3,mau);
                 d3.doanthang(*this,mau);
              }
           double DIEM::chu_vi(DIEM d2,DIEM d3)
              {
                double s;
                s=(*this).do_dai(d2)+d2.do_dai(d3)+d3.do_dai(*this);
                return s;
             }
          void main()
             {
               DIEM d1,d2,d3;
               char tb_cv[20];
               d1.nhapsl();
               d2.nhapsl();
               d3.nhapsl();
               khoidongdohoa();
               d1.ve_tam_giac(d2,d3,15);
               double s=d1.chu_vi(d2,d3);
               sprintf(tb_cv,”chu vi = %0.2”,s);
               outtextxy(10,10,tb_cv);
               getch();
               closegraph();
            }
    Một số nhận xét về đối của phương thức và lời gọi phương thức
    +) quan sát nguyên mẫu của phương thức:
    void ve_doan_thang(DIEM d2,int mau);
    sẽ thấy phương thức có 3 đối:
    Đối thứ nhất là một đối tượng DIEM do this trỏ tới
    Đối thứ 2 là đối tượng DIEM d2
    Đối thứ 3 là biến mau
    Nội dung của phương thức là vẽ đoạn thẳng đi qua các DIEM *this và d2 theo mã mầu mau.Xem thân của phương thức sẽ thấy được nội dung này:
    void DIEM::ve_doan_thang(DIEM d2,int mau)
    {
    setcolor(mau);
    line(this->x,this->y,d2.x,d2.y);
    }
    Tuy nhiên trong trường hợp này vai trò của con trỏ this không cao lắm vì nó chỉ cốt làm rõ đối thứ nhất.Trong thân của phương thức có thể bỏ từ khóa this vẫn được.
    +) Vai trò của con trỏ this trở nên quan trọng trong phương thức ve_tam_giac:
    ve_tam_giac(DIEM d2,DIEM d3,int mau);
    phương thức có 4 đối là:
    this trỏ tới một đối tượng kiểu DIEM
    d2 một đối tượng kiểu DIEM
    d3 một đối tượng kiểu DIEM
    mau là một biến nguyên
    nội dung của phương thức là vẽ 3 cạnh:
    cạnh 1 đi qua *this và d2
    cạnh 2 đi qua d2 và d3
    cạnh 3 đi qua d3 và *this
    các cạnh trên được vẽ nhờ sử dụng phương thức vẽ đoạn thẳng
    Vẽ cạnh 1 dùng lệnh: (*this).ve_doan_thang(d2,mau);
    Vẽ cạnh 2 dùng lệnh: d2.ve_doan_thang(d3,mau);
    Vẽ cạnh 3 dùng lệnh: d3.ve_doan_thang(*this,mau);
    Trong trường hợp này vai trò của this là khá quan trong do nếu không có nó thì công việc trở nên khá khó khăn,dài dòng và khó hiểu hơn. Chúng ta so sánh 2 phương án:
    Phương án dùng this trong phương thức ve_tam_giac:
    void DIEM::ve_tam_giac(DIEM d2,DIEM d3,int mau)
    {
    (*this).doanthang(d2,mau);
    d2.doanthang(d3,mau);
    d3.doanthang(*this,mau);
    }
    Phương án không dùng this trong phương thức ve_tam_giac:
    void DIEM::ve_tam_giac(DIEM d2,DIEM d3,int mau)
    {
    DIEM d1;
    d1.x=x;
    d1.y=y;
    d1.ve_doan_thang(d2,mau);
    d2.ve_doan_thang(d3,mau);
    d3.ve_doan_thang(d1,mau);

  10. #20
    Ngày gia nhập
    12 2006
    Bài viết
    72

    §5. Nói thêm về kiểu phương thức và kiểu đối của
    phương thức

    5.1.Kiểu phương thức
    Phương thức có thể không có giá trị trả về (kiểu void) hoặc có thể trả về một giá trị có kiểu bất kì, kể cả giá trị kiểu đối tượng,con trỏ đối tượng, tham chiếu đối tượng.
    5.2. Đối của phương thức
    Đối của phương thức cũng giống như đối của hàm có thể có kiểu bất kì:
    +. Dữ liệu kiểu chuẩn như int,float,char,…Con trỏ hoặc tham chiếu đến kiểu dữ liệu chuẩn như int*,float*,int&,float&,char&…
    +. Dữ liệu kiểu ngoài chuẩn đã được định nghĩa trước như đối tượng,cấu trúc,hợp, kiểu liệt kê enum…Con trỏ hoặc tham chiếu đến kiểu ngoài chuẩn này.
    +. Kiểu đối tượng của chính phương thức,con trỏ hoặc tham chiếu đến chính đối tượng này.
    5.3. Các ví dụ:
    Ví dụ 1:
    Nội dung:
    +) thuộc tính (thành phần dữ liệu) của lớp có thể là đối tượng của lớp khác đã định nghĩa bên trên.
    +) phương thức có giá trị trả về kiểu đối tượng và con trỏ đối tượng.
    Nội dung chính là nhập một dãy hình chữ nhật, sau đó tìm hình chữ nhật có max diện tích và hình chữ nhật có max chu vi
    Chương trình được tổ chức thành 2 lớp:
    +) lớp HINH_CN gồm:
    +)các phương thức tính: d và r(chiều dài và chiều rộng)
    +)các phương thức
    void nhapsl(); //nhập chiều dài, chiều rộng
    int dien_tich(); //tính diện tích
    int chu_vi(); //tính chu vi
    +) Lớp DAY_HINH_CN gồm:
    -Các thuộc tính:
    int n; // số hình chữ nhật của dãy
    HINH_CN *h; //con trỏ tới dãy đối tượng của lớp HINH_CN
    -Các phương thức:
    void nhapsl(); //Nhập một dẫy hình chữ nhật
    HINH_CN hinh_dt_max(); //Trả về hình chữ nhật có diện
    // tích max
    HINH_CN *hinh_cv_max(); // Trả về con trỏ tới HCN có
    // chu vi max
    Code:
    #include<conio.h>
     #include<iostream.h>
    class HINH_CN 
        {
          private:
              int d,r; // chieu dai va chieu rong
          public:
              void nhapsl()
                 {
                   cout<<”\n Nhap chieu dai va chieu rong: “;
                   cin>>d>>r;
                 }
           void in()
              {
                cout<<”\n chieu dai= “<<d;
                cout<<”\n chieu rong = “<<r;
              }
            int dien_tich()
              {
                return d*r;
              }
            int chu_vi()
              {
                return 2*(d+r);
              }
         };
    class DAY_HINH_CN
          {
            private:
              int n; // So hinh chu nhat
              HINH_CN *h;
            public:
              void nhapsl();
              HINH_CN hinh_dt_max();
              HINH_CN *hinh_cv_max();
          };
    void DAY_HINH_CN::nhapsl()
          {
            cout<<”\n so hinh chu nhat la: “;
            cin>>n;
            h=new HINH_CN[n+1];
              for(int i=1;i<=n;i++)
                 h[i].nhapsl();
         }
    HINH_CN DAY_HINH_CN::hinh_dt_max()
         {
           HINH_CN hdtmax;
           hdtmax=h[1];
           for(int i=2;i<=n;i++)
             if(h[i].dien_tich()>hdtmax.dien_tich())
                hdtmax=h[i];
           return hdtmax;
         }
    HINH_CN *DAY_HINH_CN::hinh_cv_max()
         {
           int imax=1;
           for(int i=2;i<=n;i++)
             if(h[i].chu_vi()>h[imax].chu_vi())
                imax=i;
           return(h+imax);
         }
    void main()
        {
          DAY_HINH_CN d;
          HINH_CN hdtmax;
          d.nhapsl();
          hdtmax=d.hinh_dt_max();
          hdtmax.in();
          HINH_CN *hcvmax=d.hinh_cv_max();
          hcvmax->in();
          getch();
        }
    Ví dụ 2 :
    + Thuộc tính (thành phần dữ liệu) có thể là đối tượng của lớp khác đã định nghĩa bên trên.
    + Phương thức có giá trị trả về kiểu đối tượng
    + Vai trò của con trỏ this (xem phương thức maxdt của lớp TAM_GIAC)
    + Phương thức tĩnh (xem phương thức tao_tg của lớp TAM_GIAC)
    Nội dung của chương trình là nhập một dãy các điểm sau đó tìm tam giác có diện tích lớn nhất tạo thành từ các điểm vừa nhập
    Chương trình được tổ chức thành 2 lớp:
    + Lớp DIEM gồm:
    . Các thuộc tính: x và y là tọa độ của các điểm
    . Các phương thức
    void nhapsl(); Nhập tọa độ cho các điểm
    void in(); In ra tọa độ các điểm
    double do_dai(DIEM d2); // tính độ dài của đoạn thẳng
    //đi qua 2 điểm,điểm ẩn xác
    //định bởi this và điểm (d2)
    + Lớp TAM_GIAC gồm:
    - Các thuộc tính:
    DIEM d1,d2,d3; // 3 đỉnh của tam giác
    -Các phương thức:
    void nhapsl(); // Nhập tọa độ 3 đỉnh
    void in(); // In tọa độ 3 đỉnh
    // Tạo một đối tượng TAM_GIAC từ 3 đối tượng DIEM
    static TAM_GIAC tao_tg(DIEM e1,DIEM e2,DIEM e3)
    double dien_tich(); //Tính diện tích
    // Tìm tam giác có diện tích max trong 2 tam giác *this và t2
    TAM_GIAC maxdt(TAM_GIAC t2);
    + Các vấn đề cần chú ý trong chương trình là:
    - Phương thức tĩnh tao_tg( sẽ giải thích bên dưới)
    - Phương thức maxdt
    + Thuật toán là:
    -Duyệt qua các tổ hợp 3 điểm.
    - Dùng phương thức tao_tg để lập tam giác từ 3 điểm
    -Dùng phương thức maxdt để chọn tam giác có diện tích lớn hơn trong 2 tam giác: tam giác vừa tạo và tam giác có diện tích max(trong số các tam giác đã tạo)
    Chúng ta bắt đầu vào chương trình:
    Code:
    #include<conio.h>
    #include<iostream.h>
    #include<math.h>
    class DIEM
      {
        private:
            double x,y; // Toa do cua diem
        public:
            void nhapsl()
              {
                cout<<” Toa do x,y cua diem: “;
                cin>>x>>y;
             }
            void in()
             {
               cout<<” x= “<<x<<” y= “<<y;
             }
            double do_dai(DIEM d2)
              {
                return sqrt(pow(x-d2.x,2)+pow(y-d2.y,2));
              }
      };
    class TAM_GIAC
        {
          private:
              DIEM d1,d2,d3; //3 dinh cua tam giac
          public:
              void nhapsl();
              void in();
         static TAM_GIAC tao_tg(DIEM e1,DIEM e2,DIEM e3)
              {
                TAM_GIAC t;
                 t.d1=e1;t.d2=e2;t.d3=e3;
                 return t;
              }
          double  dien_tich();
          TAM_GIAC maxdt(TAM_GIAC t2);
       };
    void TAM_GIAC::nhapsl()
       {
         cout<<”\n Dinh 1: “;
         d1.nhapsl();
         cout<<”\n Dinh 2: “;
         d2.nhapsl();
         cout<<”\n Dinh 3: “;
         d3.nhapsl();
       }
    void TAM_GIAC::in()
       {
         cout<<”\n Dinh 1: “;d1.in();
         cout<<”\n Dinh 2: “;d2.in();
         cout<<”\n Dinh 3: “;d3.in();
       }
    double  TAM_GIAC::dien_tich()
        {
          double a,b,c,p,s;
          a=d1.do_dai(d2);
          b=d2.do_dai(d3);
          c=d3.do_dai(d1);
          p=(a+b+c)/2;
          return sqrt(p*(p-a)*(p-b)*(p-c));
        }
    TAM_GIAC TAM_GIAC::maxdt(TAM_GIAC t2)
        {
          if(this->dien_tich()>t2.dien_tich())
            return *this;
         else
            return t2;
        }
    void main()
         {
           DIEM d[50];
           int n,i;
           clrscr();
           cout<<”\n So diem= “;
           cin>>n;
           for(i=1;i<=n;i++)
             {
              cout<<”\n Nhap diem: “<<i<<” : “;
              d[i].nhapsl();
             }
           int j,k;
          TAM_GIAC tmax,t;
          tmax=TAM_GIAC::tao_tg(d[1],d[2],d[3]);
          for(i=1;i<=n-2;i++)
             for(j=i+1;j<=n-1;j++)
               for(k=j+1;k<=n;k++)
                  {
                    t=TAM_GIAC::tao_tg(d[i],d[j],d[k]);
                    tmax=tmax.maxdt(t);
                  }
          cout<<”\n\n Tam giac co dien tich lon nhat: “;
          tmax.in();
          cout<<”\n Dien tich = “<<tmax.dien_tich();
          getch();
      }
    Vì bài này khá dài nên em chia thành 2 phần cho mọi người dễ theo dõi,trên đây là phần 1,hôm sau sẽ có phần 2.
    HAPPY NEW YEAR!

Các đề tài tương tự

  1. Bán Chung Cư Văn Phú Victoria Văn Phú 96m2 16tr/m2
    Gửi bởi nguyentiennhien trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 0
    Bài viết cuối: 23-11-2012, 01:11 PM
  2. Biệt thự văn phú, liền kề văn phú, căn góc, hướng đẹp, giá ưu đãi
    Gửi bởi ephat_tt86 trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 0
    Bài viết cuối: 14-12-2011, 09:12 AM
  3. Liền kề văn phú, dự án văn phú, giá bán = gốc + chênh thấp
    Gửi bởi ephat_tt86 trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 0
    Bài viết cuối: 07-12-2011, 11:40 AM
  4. Liền kề Văn Phú TT28, bán Liền kề Văn Phú TT2, TT9, TT12, TT33
    Gửi bởi datphat191 trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 0
    Bài viết cuối: 20-10-2011, 12:10 PM
  5. Cần Bán Chung Cư Văn Phú Victoria- Văn Phú Hà Đông
    Gửi bởi huybm90 trong diễn đàn Giới thiệu website, sản phẩm của bạn
    Trả lời: 1
    Bài viết cuối: 03-10-2011, 10:42 AM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn