LẬP TRÌNH C/C++ NÂNG CAOYêu cầu trước khi đọc: học xong Lập trình C/C++ căn bảnBÀI 1: NHẮC LẠI VỀ C/C++Nhập xuất cơ bảnCOD...
gọi:add10(n);Không hiệu quả, a vẫn giữ nguyên giá trịCách 2:CODEvoid add10(int *a){    *a=*a+10;}gọi:add10(&n);Hiệu quả.Cá...
CODEvoid input(int[]);input(a);void input(int *a){    for(int i=0;i<3;++i)        scanf("%d",&(*(a+i)));}void output(int[]...
input(a);void input(int a[]){    for(int i=0;i<3;++i)        cin>>a[i];}void output(int[]);output(a);void output(int a[]){...
Xuất mảng số thực 2 chiều bằng cách dùng mallocCODEfloat** p;p=(float**)malloc(2);for(int i=0;i<3;i++)    for(int j=0;j<2;...
BÀI 2: NHẮC LẠI VỀ C/C++ (TIẾP THEO)Cấu trúc (struct)Con trỏ cấu trúc (struct pointer)CODEstruct Student{    int id;};Stud...
void add(Student &s){    cin>>s.name;    cin.get();    cin>>s.id;}add10(a);Cách 3CODEvoid add(Student *s){    cin>>(*s).na...
        (*((char*)data))++;break;    case sizeof(int):        (*((int*)data))++;break;    }}int main(){    char c=66;int a...
int number;ifstream inf;ofstream outf;inf.open("input.txt");outf.open("output.txt");while(in>>number)outf<<"Next is"<<setw...
BÀI 3: NHẮC LẠI VỀ LỚPCơ bản về lớpCODEclass Date{    int day;public:    Date(int,int a=1);    int month;    void setDay(i...
Hàm bạn (friend function)CODEclass Student{public:    int id;    friend bool equal(const Student&,const Student&);};int ma...
    return outs;}int main(){    Date d;    cin>>d;cout<<d;    Date *dt=new Date;    //phải tạo object pointer, cấp phát bộ...
Chú ý về cấp phát bộ nhớÐiều gì sẽ xảy ra khi chúng ta không thể cấp phát bộ nhớ ? Ví dụ chúng ta viết 1 game RTS mà mỗi p...
        c = new char[strlen(src.c)+1];        strcpy(this->c,src.c);    }    return *this;}bool operator!=(const Base& b1,...
public:    virtual void Play() = 0;};class DVD:public MusicPlayer{public:    void Play(){        cout<<"Play on DVD"<<endl...
string s1,s2;s1+=s2;s1+=o;s1.append(s2);        //y nhu s1+=s2s1.append(s2,3,string::npos);        //thêm vào s1 từ kí tự ...
BÀI 4: TEMPLATEHàm templateGiả sử chúng ta cần viết một hàm trả về số nguyên lớn nhất giữa 2 sốCODEint maximum(int a,int b...
CODEint a[3],b[3];cout<<maximum(a,b);Vậy phải làm sao ?(Trong lập trình, những vấn đề tưởng như nhỏ nhặt thế này thực ra g...
BÀI 5: TEMPLATE (TIẾP)Lại đau đầuTa muốn viết một chương trình tìm kiếm phần tử trong một mảng. Ta viết như sauCODEtemplat...
}template<typename T>void Array<T>::makeArray(T *&arr,int n){    arr = new T[n];}template<typename T>T& Array<T>::operator...
template class như trên thử xem.Bạn sẽ không viết được đâu nếu không sử dụng cái này: prototype template function (khai bá...
Cuối cùng là “main.cpp”CODE#include "array.h"class Person{    int age;public:    Person()    {        age=0;    }    Perso...
Trong phần trước ta đã xem các ví dụ dùng cách “tham chiếu mà tham chiếu đến con trỏ” Trong phần này chúng ta sẽ overloadt...
bool operator!=(const Array<T>& a1,const Array<T>& a2){    if(a1.size!=a2.size) return true;    else for(int i=0;i<a1.size...
BÀI 6: TEMPLATE (TIẾP THEO)Trình biên dịch và templateTrong bài trước chúng ta thấy một điều hơi là lạ, đó là file header ...
    public:    pair(T first, T second) {        value1=first; value2=second;    }    T module() {return 0;}};//viết lại đị...
Đối số kiểu primitive, ví dụ kiểu intCODEtemplate<typename T,int size>class Array{    T* array;public:    Array();};templa...
Làm cái bài tập chứ nhỉ. Đề đơn giản thôi: lập trình một danh sách liên kết đơn dùng template, đủ các phép thêm, xóa, sửa,...
};
BÀI 7: CONST, STATIC, EXCEPTION, CASTING (BIẾN HẰNG, BIẾN TĨNH, NGOẠI LỆ, ÉP KIỂU)CONSTconst int p và int const p là như n...
hello() = 3;Nên chúng ta phải thay đổi để nó trả về constCODEconst int& hello(){    int temp=5;    return temp;}Bảo vệ tha...
CODEclass MyClass{public:static void print(char* s){cout<<s<<endl;}};int main(){    MyClass::p("Hello");    return 0;}Ngoạ...
Khi chúng ta bắt được một ngoại lệ, chúng ta có thể ném nó ra để bắt lại một lần nữa bằng throw;CODEclass DivideByZero{pub...
}Chúng ta có thể ném ra nhiều thứ bằng một chỉ thị try và bắt lại tất cả mọi thứ, mỗi thứ một chỉ thị catch khác nhauCODEi...
CODEclass Base{};class Derived : public Base{};Base* b=new Derived;Derived* d = static_cast<Derived*>(b);bằng dynamic_cast...
reinterpret_cast (ép kiểu thông dịch lại)reinterpret_cast sẽ ép kiểu bất cứ con trỏ hay đối tượng nào mà không hề có sự ki...
BÀI 8: STL - SEQUENTIAL CONTAINERYêu cầu: học xong môn cấp trúc dữ liệu và giải thuật cơ bản hoặc tương đương để có kiến t...
CODElist<string> list1;list1.push_back("Zebra");list1.push_back("Penguin");list1.push_front("Lion");list<string>::iterator...
#include <vector>vector giống list ngoại trừ-cho phép random access, với operator[], nghĩa là v[5], v[6], etc như mảng-đượ...
vector<int> v, v2;merge(v1.begin(),v1.begin()+5,++v2.begin(),v2.end(),v1.begin());//hợp 2 phần dữ liệu, phần một từ v1.beg...
BÀI 9:FUNCTION OBJECT (ĐỐI TƯỢNG HÀM)Function objectMột function object (đối tượng hàm) là một object (đối tượng) được sử ...
    {        cout<<"Integer:"<<i<<endl;    }    void operator()(float f) const    {        cout<<"Float:"<<f<<endl;    }};...
    list<int>::iterator i=find_if(l.begin(),l.end(),IsOdd);    if(i!=l.end()) cout<<*i;}Hàm find_if tìm từ phần tử v.begin...
Hàm searchHàm search tìm vị trí của một chuỗi con trong một chuỗi lớn hơn, nếu tìm thấy thì trả về iterator trỏ đến vị trí...
{public:    void operator()(const T& t){cout<<t<<endl;}};int main(int argc, char* argv[]){    list<string> l;l.push_back("...
BÀI 10:THƯ VIỆN FUNCTIONALCODE#include <functional>Hạng của một predicateCó nhiều sự mập mờ do từ đồng nghĩa giữa các khái...
Cấu trúc binary_function trong thư viện functionalTương tự, trong thư viện functional đã định nghĩa sẵn cấu trúc binary_fu...
với đối số còn lại của binary predicate ban đầu trở thành đối số của unary predicate kết quảCODEclass compare:public binar...
CODEvector<int> v;Hàm này có 2 phiên bảnSắp xếp lại một chuỗi phần tử theo thứ tự tăng dần (ascending)CODEsort (v.begin(),...
CODEint a[]={1,-2,3};transform(a,a+3,a,negate<int>());for_each(a,a+3,Output<int>());Ví dụ plusCODEint a[]={1,2,3,4,5};int ...
BÀI 11 – ASSOCIATIVE CONTAINER (CÁC BỘ LƯU TRỮ LIÊN KẾT)Bao gồm map (ánh xạ) multimap (đa ánh xạ) set (tập hợp) multiset (...
mapPerson.insert(pr);value_type thực chất cũng là một pairComparatorMột functor dùng để so sánh, sắp xếp, etc các phần tử ...
for(MII i=m.begin();i!=m.end();++i)    cout<<(*i).first<<" "<<(*i).second<<endl;Chạy thử bạn sẽ thấy các value trong map đ...
    cout<<(*i)<<endl;set không có operator[]multisetmultiset cũng giống set ngoại trừ một điều, mỗi key có thể ánh xạ đến ...
BÀI 12: CÁC BỘ TƯƠNG THÍCH VÀ CÁC THƯ VIỆN KHÁCcontainer adapter (các bộ tương thích lưu trữ)Bao gồm stack, queue và prior...
priority_queuepriority_queue là queue trong đó phần tử đầu tiên luôn luôn là phần tử lớn nhất theo một tiêu chuẩn sắp xếp ...
    cout<<count_if(a,a+5,not1(IsOdd()))<<endl;    return 0;}not2Đổi giá trị trả về của một binary predicate từ false thành...
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
Nang cao c++
of 76

Nang cao c++

C++ nâng cao
Published on: Mar 3, 2016
Published in: Education      
Source: www.slideshare.net


Transcripts - Nang cao c++

  • 1. LẬP TRÌNH C/C++ NÂNG CAOYêu cầu trước khi đọc: học xong Lập trình C/C++ căn bảnBÀI 1: NHẮC LẠI VỀ C/C++Nhập xuất cơ bảnCODE#define max(a,b) (a>b)?a:b    //khai báo macrotypedef unsigned int byte;    //định nghĩa kiểu dữ liệuconst float PI=3.14;         //khai báo hằng sốchar c;char s[20];Cách của CCODE//không dùng scan nếu muốn nhập khoảng trắnggets(s);            //có thể nhập khoảng trắngputs(s);fflush(stdin);        //xóa bộ đệm nhậpc=getchar();putchar©;Cách của C++CODE//không dùng cin>> nếu muốn nhập khoảng trắngcin.getline(a,21);    //có thể nhập khoảng trắngcout<<a;cin.get();        //xóa bộ đệm nhậpCon trỏ cơ bảnCODEint a=5,*p;//p=3;            //khong hop ve vi khong the gan gia tri kieu int cho bien kieu int*//&p=3;        //khong hop le vi dia chi cua p la co dinhp=&a;            //hop le, gan dia chi ma p tro den*p=3;            //hop le, gan gia tri tai dia chi ma p tro dencout<<p<<endl;    //cai gi do bat ki, dia chi cua acout<<&p<<endl;    //cai gi do bat ki, dia chi cua pcout<<*p<<endl;    //3,dau * luc nay mang y nghia "gia tri tai dia chi cua"Truyền giá trị cho hàmTrong C có khái niệm con trỏ (pointer) Trong C++ có thêm khái niệm tham chiếu (reference)CODEint a;int& b=a;Lúc này biến a có một cái nickname là bNhư vậy có tất cả 3 cách viết hàm và truyền tham sốCách 1:CODEvoid add10(int a){    a=a+10;}
  • 2. gọi:add10(n);Không hiệu quả, a vẫn giữ nguyên giá trịCách 2:CODEvoid add10(int *a){    *a=*a+10;}gọi:add10(&n);Hiệu quả.Cách 3:CODEvoid add10(int &a){    a=a+10;}gọi:add10(n);Hiệu quả, tiện hơn cách 2.Nhập xuất dữ liệu với kiểu mảng số nguyênCODEint a[3];Truyền dữ liệu trực tiếp theo kiểu C, cách 1CODEfor(int i=0;i<3;++i) scanf("%d",&(*(a+i)));for(int i=0;i<3;++i) printf("%d",*(a+i));Truyền dữ liệu trực tiếp theo kiểu C, cách 2CODEfor(int i=0;i<3;++i) scanf("%d",&a[i]);for(int i=0;i<3;++i) printf("%d",a[i]);Truyền dữ liệu trực tiếp theo kiểu C++, cách 1CODEfor(int i=0;i<3;++i) cin>>*(a+i);for(int i=0;i<3;++i) cout<<*(a+i);Truyền dữ liệu trực tiếp theo kiểu C++, cách 2CODEfor(int i=0;i<3;++i) cin>>a[i];for(int i=0;i<3;++i) cout<<a[i];Nhập xuất dữ liệu bằng hàm với kiểu mảng số nguyênNhập xuất dữ liệu bằng hàm với kiểu mảng số nguyên theo kiểu C, cách 1
  • 3. CODEvoid input(int[]);input(a);void input(int *a){    for(int i=0;i<3;++i)        scanf("%d",&(*(a+i)));}void output(int[]);output(a);void output(int *a){    for(int i=0;i<3;++i)        printf("%d",*(a+i));}Nhập xuất dữ liệu bằng hàm với kiểu mảng số nguyên theo kiểu C, cách 2CODEvoid input(int[]);input(a);void input(int a[]){    for(int i=0;i<3;++i)        scanf("%d",&a[i]);}void output(int[]);output(a);void output(int a[]){    for(int i=0;i<3;++i)        printf("%d",a[i]);}Nhập xuất dữ liệu bằng hàm với kiểu mảng số nguyên theo kiểu C++, cách 1CODEvoid input(int[]);input(a);void input(int *a){    for(int i=0;i<3;++i)        cin>>*(a+i);}void output(int[]);output(a);void output(int *a){    for(int i=0;i<3;++i)        cout<<*(a+i);}Nhập xuất dữ liệu bằng hàm với kiểu mảng số nguyên theo kiểu C++, cách 2CODEvoid input(int[]);
  • 4. input(a);void input(int a[]){    for(int i=0;i<3;++i)        cin>>a[i];}void output(int[]);output(a);void output(int a[]){    for(int i=0;i<3;++i)        cout<<a[i];}Nhập xuất dữ liệu với kiểu mảng số thựcCách dùng biến tạmCODEfloat a[2][3],temp;for(int i=0;i<2;++i)    for(int j=0;i<3;++j)    {        scanf("%f n",&temp);        a[i][j]=temp;    }Cách dùng con trỏCODEfloat a[2][3];float *p;p=(float*)a;for(int i=0;i<2*3;++i)    scanf("%f",(p+i));Nhập mảng số thực 2 chiều bằng cách dùng ép kiểuCODEfloat a[3][2];float *p;p=(float*)a;for(int i=0;i<3;i++)    for(int j=0;j<2;j++)        scanf("%f",((float*)p+i*2+j));Xuất mảng số thực 2 chiều bằng cách dùng ép kiểuCODEfloat a[3][2];float *p;p=(float*)a;for(int i=0;i<3;i++)    for(int j=0;j<2;j++)        printf("%fn",*(p+i*2+j));Nhập mảng số thực 2 chiều bằng cách dùng mallocCODEfloat** p;p=(float**)malloc(2);for(int i=0;i<3;i++)    for(int j=0;j<2;j++)        scanf("%f",(p+i*2+j));
  • 5. Xuất mảng số thực 2 chiều bằng cách dùng mallocCODEfloat** p;p=(float**)malloc(2);for(int i=0;i<3;i++)    for(int j=0;j<2;j++)        printf("%fn",*(p+i*2+j));Bài này chỉ có giá trị tham khảo, tổng hợp kiến thức.
  • 6. BÀI 2: NHẮC LẠI VỀ C/C++ (TIẾP THEO)Cấu trúc (struct)Con trỏ cấu trúc (struct pointer)CODEstruct Student{    int id;};Student *s;Student m;s=&m;s->id=3;    //means (*s).idcout<<m.id;Sao chép cấu trúcCODEstruct Student{    int id;    char *name;        //một con trỏ, không phải một mảng};Student a;char temp[20];cin>>temp;a.name=new char[strlen(temp)+1];strcpy(a.name,temp);        //phải dùng biến tạmStudent b=a;strcpy(b.name,a.name);    //phải dùng strcpy, nếu không sẽ sao chép địa chỉ bộ nhớGọi hàm với cấu trúcCODEstruct Student{    char name[10];    int id;};Student m[3],a;m[0]=(Student){"Pete",1};add(m[0].name,&m[0].id);Có 4 cách để thêm dữ liệu vào cấu trúc.Cách 1CODEvoid add(char name[],int *place){cin>>name;cin.get();cin>>*place;}add(a.name,&a.id);Cách 2CODE
  • 7. void add(Student &s){    cin>>s.name;    cin.get();    cin>>s.id;}add10(a);Cách 3CODEvoid add(Student *s){    cin>>(*s).name;    cin.get();    cin>>(*s).id;}add(&a);Cách 4CODEvoid add(Student *s){    cin>>s->name;    cin.get();    cin>>s->id;}add(&a);Toán tử sizeof với structCODEstruct Hello{    char c;    double d;};sizeof(Mystruct)=12; vì c lấy một 32-bit word (4 byte, không phải 1 byte)Con trỏ (pointer)Con trỏ trỏ đến một con trỏ khácCODEchar a=z;    //a=z và giả sử địa chỉ của a=8277char *p=&a;    //p=8277 và giả sử địa chỉ của p=6194char **p2=&p;    //p2=6194 và địa chỉ của p2 sẽ là một cái gì đóCon trỏ void (void pointer)Con trỏ void dùng để trỏ đến bất cứ một kiểu dữ liệu nàoCODEvoid increase(void* data,int dataType){    switch(dataType)    {    case sizeof(char):
  • 8.         (*((char*)data))++;break;    case sizeof(int):        (*((int*)data))++;break;    }}int main(){    char c=66;int a=-4;    increase(&c,sizeof(char));    increase(&a,sizeof(int));}Con trỏ hàm (function pointer)Con trỏ hàm dùng để trỏ đến một hàmCODEint addition(int a,int b){    return a+b;}int subtraction(int a,int b){    return a-b;}int (*minuse)(int,int) = subtraction;int primi(int a,int b,int(*functocall)(int,int)){    return (*functocall)(a,b);}int main(){    int m=primi(7,5,&addition);    int n=primi(20,m,minuse);    cout<<m<<endl;cout<<n<<endl;    return 0;}Hàm nội tuyến (inline function)Hàm khai báo với từ khóa inline, trình biên dịch sẽ chèn toàn bộ thân hàm mỗi nơi mà hàm đó được sử dụng. Với cách này, cáchàm inline có tốc độ thực thi cực nhanh, nên sử dụng với các hàm thường xuyên phải sử dụng trong chương trình.CODEinline void display(char *s){    cout<<s<<endl;}int main(){    display("Hello");return 0;}Nhập xuất với tập tinCODE#include <fstream>#include <iomanip>
  • 9. int number;ifstream inf;ofstream outf;inf.open("input.txt");outf.open("output.txt");while(in>>number)outf<<"Next is"<<setw(4)<<number<<endl;inf.close();outf.close();Mở một file dùng cho cả nhập và xuấtCODEfstream f;f.open("st.txt",ios :: in | ios :: out);một số chế độ hay dùngios :: in nghĩa là nhập vàoios:out nghĩa là xuất ra tập tin từ đầu tập tinios::app nghĩa là thêm dữ liệu vào tập tin (appending)Tập tin headerTạo một tập tin header có tên là myfile.h#ifndef MYFILE_H#define MYFILE_H……#endiftrong tập tin cpp thêm vào dòng#include "myfile.h"
  • 10. BÀI 3: NHẮC LẠI VỀ LỚPCơ bản về lớpCODEclass Date{    int day;public:    Date(int,int a=1);    int month;    void setDay(int);    void output();};int main(){    Date d(6);    d.month=3;    d.setDate(25);    d.output();    return 0;}Date::Date(int day,int month){    this->day=day;    this->month=month;}void Date::setDay(int day){    this->day=day;}void Date::output(){    cout<<day<<"/"<<month;}Hàm khởi tạoChúng ta có thể viết một hàm khởi tạo như thế nàyCODEclass Student{    string name;int age;public:    Student(string name,int n):name(name),age(n)    {    }};Nó tương đương vớiCODEclass Student{    string name;int age;public:    Student(string name,int n)    {        (*this).name = name;        this->age = n;    }};
  • 11. Hàm bạn (friend function)CODEclass Student{public:    int id;    friend bool equal(const Student&,const Student&);};int main(){    Student s1;s1.id=2;    Student s2;s2.id=3;    cout<<equal(s1,s2);}bool equal(const Student& s1,const Student& s2){    return (s1.id==s2.id);}Overload toán tử (operator overload)Ví dụ dưới sẽ overload toán tử ==CODEclass Student{public:    int id;    friend bool operator==(const Student&,const Student&);};int main(){    Student s1;s1.id=2;    Student s2;s2.id=3;    cout<<((s1==s2)?"equal":"unequal");}bool operator==(const Student& s1,const Student& s2){    return (s1.id==s2.id);}Overload toán tử nhập và xuất (input >> và output <<)Mọi người đều biết cin>>a là gọi toán tử nhập cin.operator>>(a) hoặc operator>>(cin,a) Overload 2 toán tử nhập và xuất này hếtsức quan trọng về sau. Nhân tiện mỗi khi cấp phát bộ nhớ, dùng xong phải luôn hủy đi để thu hồi lại bộ nhớ đã cấp phát. Vì về saugame cái ưu tiên hàng đầu là bộ nhớ, đừng để lại rác.CODEclass Date{public:    int day;int month;    friend istream& operator>>(istream&,Date&);    friend ostream& operator<<(ostream&,const Date&);};istream& operator>>(istream& ins,Date& d){    ins>>d.day;    ins>>d.month;    ins.get();    //phải xóa bộ đệm    return ins;}ostream& operator<<(ostream& outs,const Date& d){    outs<<d.day<<"/"<<d.month;
  • 12.     return outs;}int main(){    Date d;    cin>>d;cout<<d;    Date *dt=new Date;    //phải tạo object pointer, cấp phát bộ nhớ    cin>>*dt;cout<<*dt;    delete dt;        //phải hủy  object pointer}Hàm hủy (destructor)CODEclass myclass{public:    int *p;    myclass();    ~myclass();};int main(){    myclass m;    return 0;}myclass::myclass(){    p=new int;    //phải cấp phát bộ nhớ để tránh segmentation fault}myclass::~myclass(){    delete p;}Hàm khởi tạo sao chép (copy constructorCODEclass Date{public:    int day;int month;char *special;    Date(int,int,char*);    Date(const Date&);    ~Date(){        delete [] special;    //bởi vì chúng ta cấp phát bộ nhớ cho nó    }};Date::Date(int day,int month,char *special){    this->day=day;this->month=month;this->special=special;}Date::Date(const Date& d){    this->day=d.day;this->month=d.month;    this->special=new char[strlen(d.special)+1];        //cấp phát bộ nhớ cho nó    strcpy(this->special,d.special);        //phải  dùng strcpy với char array}int main(){    Date d1(29,8,"birthday");    Date d2(d1);    cout<<d2.special;    return 0;}
  • 13. Chú ý về cấp phát bộ nhớÐiều gì sẽ xảy ra khi chúng ta không thể cấp phát bộ nhớ ? Ví dụ chúng ta viết 1 game RTS mà mỗi phe tham chiến có 10 tỉquân ?Giải quyết khi không thể cấp phát bộ nhớ thành côngChúng ta vẫn thường cấp phát bộ nhớ như sauCODEchar *p;int i;cout<<"number of element u want:";cin>>i;p=new char[i+1]; delete [] p;Nếu chúng ta không thể cấp phát bộ nhớ ? CPP sẽ ném (throw) ra một ngoại lệ. Có 2 cách để xử lí chuyện nàyCách một là dùng từ khóa nothrow. Vì thế CPP vẫn tạo ra một pointer nhưng là 0CODEp=new (nothrow) char[i+1];if(p==0) cout<<"Cant allocate memory";Cách hai là bắt cái ngoại lệ ấy, Ðó là ngoại lệ std::bad_allocCODEtry{    p=new char[i+1];}catch(std::bad_alloc &mae){    cerr<<"failed to allocate memory"<<mae.what();    exit(1);}Cấp phát bộ nhớ trong CÐừng có chỉ mê new và delete không thôi, cấp phát với cách của C vẫn phải dùng về sau đấyCODEchar *p;int i;printf("number of element u want:");scanf("%d",&i);p=(char*)malloc(i+1);if(p==NULL) exit(1);free(p);hoặc chúng ta có thể dùng callocp=(char*)calloc(i,sizeof(char));Toán tử gán (assignment operator)CODEclass Base{public:    Base& operator=(const Base&);    friend bool operator!=(const Base&,const Base&);private:    char* c;};Base& Base::operator=(const Base& src){    if(*this!=src){        //to avoid self-assignment        delete [] c;
  • 14.         c = new char[strlen(src.c)+1];        strcpy(this->c,src.c);    }    return *this;}bool operator!=(const Base& b1,const Base& b2){    return(strcmp(b1.c,b2.c));}Và chúng ta có thể gọi toán tử nàyBase s2=s1;Thừa kế (inheritance)Trong C có thể sinh ra bug, trong C++ chúng sẽ được thừa kế.CODEclass Base{protected:    int id;    Base(int id){        this->id=id;    }};class Sub:public Base{public:    int code;    Sub(int code,int id):Base(id){        this->code=code;    }};Hàm ảo (virtual function)Hàm Play trong lớp MusicPlayer là một hàm ảo (virtual function)CODEclass MusicPlayer{public:    virtual void Play(){        cout<<"Play on what ?"<<endl;    }};class DVD:public MusicPlayer{public:    void Play(){        cout<<"Play on DVD"<<endl;    }};int main(){    MusicPlayer m;m.Play();    DVD d(2);d.Play();}Bây giờ chúng ta sẽ làm hàm Play trong lớp MusicPlayer là một hàm thuần ảo (pure virtual function), đồng thời làm lớp MusicPlayertrở thành một lớp trừu tượng (abstract class), chúng ta sẽ không thể tạo instance của nó được nữaCODEclass MusicPlayer{
  • 15. public:    virtual void Play() = 0;};class DVD:public MusicPlayer{public:    void Play(){        cout<<"Play on DVD"<<endl;    }};int main(){    DVD d(2);d.Play();}Chúng ta tạo con trỏ để trỏ đến các subclass của nóCODEMusicPlayer *m=new DVD(5);m->play();Chúng ta cung có thể tạo mảng các con trỏ của một lớp trừu tượngCODEclass MusicPlayer... là một lớp trừu tượngclass DVD:public MusicPlayer...class CD:public MusicPlayer...MusicPlayer *m[2];m[0]=new DVD(5);m[0]->play();m[1]=new CD("Sony");m[1]->play();Nhắc lại một chút về mảng các kí tự (char array)CODEchar destArray[10];char srcArray[]="panther";strcpy(destArray, srcArray);strcpy(destArray, srcArray,strlen(srcArray));strcat(s1,s2);        //thêm (append) s2 vào s2strncat(s1,s2,n);    //thêm (append) n kí tự đầu tiên của s2 vào s1strlen(char *s);    //độ dài (length) của char array, không bao gồm "end of char array maker"char *a;char b[];strcmp(a,b);        //trả về 0 nếu bằng,-1 nếu a<b,1 nếu a>batoi, atof, atoll convert một char array thành integer, float hay long, 3 hàm này trong stdlib.hchar *s = "123.45";int i=atoi(s);float f=atof(s);Nhắc lại một chút về chuỗi (string)CODEusing std::string;*khởi tạo (constructor)string s1;string s2("Hello boy");string s3(s2);string s4(s2,3,4);    //sao chép từ kí tự thứ 3, sao chép 4 kí tựstring s5(8,*);    //khởi tạo chuỗi gồm toàn dấu **toán tử gán (assignment)string s4=s2;string s5.assign(s3);*so sánh chuỗi (compare string)if(s1==s2)        //bây giờ có thể dùng == rồiif(s1.compare(s2))*cộng chuỗi
  • 16. string s1,s2;s1+=s2;s1+=o;s1.append(s2);        //y nhu s1+=s2s1.append(s2,3,string::npos);        //thêm vào s1 từ kí tự thứ 3 đến hết s2s1.insert(7,s2);        //thêm s2 vào sau kí tự thứ 7 của s1*kích cỡ (capacity)s.capacity() trả về kích cỡ tối đaif s.size()=15, s.capacity()=16 (16-byte)if s.size()=17, s.capacity()=32 (two 16-byte)*truy xuất chuỗi#include <stdexcept>try{    cout<<s.at(100);}catch(out_of_range& e){    cout<<"invalid index";}
  • 17. BÀI 4: TEMPLATEHàm templateGiả sử chúng ta cần viết một hàm trả về số nguyên lớn nhất giữa 2 sốCODEint maximum(int a,int b){    return (a>b)?a:b;}Rồi đến số thực chúng ta cũng làm như vậyCODEdouble maximum(double a,double b){    return (a>b)?a:b;}Rồi giả sử như với lớp Person chúng ta cũng phải làm như vậy (toán tử > đã được overload)CODEPerson maximum(Person a,Person b){    return (a>b)?a:b;}C++ cung cấp một giải pháp cho vấn đề này, đó là templateCODEtemplate<class T>T maximum(T a,T b){    return (a>b)?a:b;}int main(){    int a=7;int b=5;    cout<<maximum(a,b);return 0}template với nhiều hơn một kiểu dữ liệuCODEtemplate<class T,typename U>void func(T a,U b);Dùng template với mảngCODEtemplate<class T,int size>void print(T (&a)[size]){    for(int i=0;i<size;i++) cout<<a[i]<<endl;}Lớp template (template class)CODEtemplate<class T>class pair{    T values[2];public:    pair(T first,T second)    {        values[0]=first; values[1]=second;    }    T getmaximum();};template<class T>T pair<T>::getmaximum(){    return (values[0]> values[1])? values[0]: values[1];}Trong hàm mainCODEpair<int> myobject(155,36);myobject.getmaximum();Thật tuyệt, đúng không ?Vấn đề không đơn giản như vậy.Đau đầuXem lại hàm template dưới đâyCODEtemplate<class T>T maximum(T a,T b){    return (a>b)?a:b;}Ví dụ dưới đây thực ra là đang so sánh địa chỉ bộ nhớ (memory address) của 2 biến a và bCODEchar* a = "hello";char* b = "world";cout<<maximum(a,b);Ví dụ dưới đây cũng là đang so sánh địa chỉ bộ nhớ (memory address) của 2 biến a và bdiv, id: post-25916, class: postcolor
  • 18. CODEint a[3],b[3];cout<<maximum(a,b);Vậy phải làm sao ?(Trong lập trình, những vấn đề tưởng như nhỏ nhặt thế này thực ra gây đau đầu lắm đó, nhất là khi phải làm dự án từ 1000 words trở lên. Mà đặc biệt riêng lập trình game đụng những chuyện đau đầu này thường xuyênhơn các phân ngành IT khác. Biên dịch thành công, mà tại sao nó … kì cục vầy nè ?)Cứu tinh xuất hiện, đó là một tham chiếu mà tham chiếu đến một con trỏ (a reference which refers to a pointer). Đây là dạng đau đầu nhất của tham chiếu.A reference which refers to a pointerCODEint* p;    //một con trỏ p bình thườngint*& r = p;    //tham chiếu r là nickname mới của pr = new int;    //tương đương với p = new int*r = 5;        //tương đưong với *p = 5cout<<*p;        //tương đương với cout<<*rVà như vậy, vấn đề khó khăn với dữ liệu kiểu mảng đã được giải quyết.CODEtemplate<class T>T* maximum(T*& a,T*& b){    return (*a>*b)?a:b;}int main(){    char* a="bb";    char* b="aa";    cout<<maximum(a,b);    return 0;}Lưu ý là chỉ có "một tham chiếu mà tham chiếu đến một con trỏ" và "một con trỏ mà trỏ đến một con trỏ khác", chứ không thề có những khái niệm như "một tham chiếu mà tham chiếu đến một tham chiếu khác" hay"một con trỏ mà trỏ đến một tham chiếu" đâu nhá.Hết khó khăn chưa ? Chưa đâu.
  • 19. BÀI 5: TEMPLATE (TIẾP)Lại đau đầuTa muốn viết một chương trình tìm kiếm phần tử trong một mảng. Ta viết như sauCODEtemplate<class T>int search(T a[],int n,T key){    int index=0;    while(index<n && a[index] != key) index++;    if(index == n) return -1;else return index;}Sau đó trong hàm main ta viếtCODEchar *list[]={"zero","one","two"};    //thực ra là mảng 2 chiều thôisearch(list,3,"two");    //ồ không, lại so sánh memory address nữa rồiNhưng lần này vấn đề phức tạp hơn nhiều. Ví dụ nếu là mảng các Person là đụng thêm vấn đề cấp phát bộ nhớ nữaGiải quyếtChương trình dưới đây trình bày cách tạo một lớp mảng template, với đủ các chức năng tạo, thêm, truy xuất dữ liệu, toán tử [].Đặc biệt là giải quyết đau đầu tìm kiếm dữ liệu ở trên vì so sánh memory address. Lưu ý là khi tạo ta phải dùng reference refers topointer để cấp phát bộ nhớ đóCODE#include <iostream>using namespace std;template<class T>class Array{    T* array;int size;public:    Array(int n);    ~Array();    void setValue(const T&,int n);    //thiết lập dữ liệu    T& getValue(int n);        //truy xuất dữ liệu    void makeArray(T *&arr,int n);    //tạo mảng    T& operator[](int i);        //toán tử [] truy xuất dữ liệu mảng    int seek(const T& key);    //tìm kiếm trong mảng gọi hàm    int search(const T* list,int size,const T key);        //tìm kiếm trong mảng có sẵn};template<typename T>Array<T>::Array(int n){    size=n;    array = new T[size];}template<typename T>Array<T>::~Array(){    delete [] array;}template<typename T>void Array<T>::setValue(const T& value,int n){    *(array+n) = value;}template<typename T>T& Array<T>::getValue(int n){    return *(array+n);
  • 20. }template<typename T>void Array<T>::makeArray(T *&arr,int n){    arr = new T[n];}template<typename T>T& Array<T>::operator[](int i){    return *(array+i);}template<typename T>int Array<T>::seek(const T& key){    int index=0;    while((index<size) && *(array+index)!=key) ++index;    if(index==size) return -1;    else return index;}template<typename T>int Array<T>::search(const T* list,int size,const T key){    int index=0;    while((index<size) && *(list+index)!=key) ++index;    if(index==size) return -1;    else return index;}class Person{    int age;public:    Person(){age=0;}    Person(int age){this->age=age;}    int getAge() const{return age;}    friend bool operator!=(const Person& p1,const Person& p2)    {        return p1.getAge()!=p2.getAge();    }    friend ostream& operator<<(ostream& os,const Person& p)    {        os<<p.getAge()<<endl;        return os;    }};int main(){    Array<Person> a(3);    a.setValue(Person(5),2);    cout<<a[2];    Person* b;    a.makeArray(b,4);    for(int i=0;i<4;i++) *(b+i)=Person(i+2);    cout<<a.seek(Person(5))<<endl;    cout<<a.search(b,4,Person(4))<<endl;    return 0;}Có vẻ đã xong. Hết rắc rối rồi.Chưa. Vẫn còn 2 rắc rối nữa. Bạn hãy thử viết toán tử output << cho một mảng template class hay so sánh giữa hai mảng
  • 21. template class như trên thử xem.Bạn sẽ không viết được đâu nếu không sử dụng cái này: prototype template function (khai báo nguyên mẫu cho hàm template)(Học mấy cái điên đầu này làm gì nhỉ ? Làm gì à ? Hãy thử cho hai cầu thủ trong một game đá banh đối diện nhau. Họ có baonhiêu hành động có thể làm được lúc đó ? Chuyền bóng ? Lừa bóng ? Đốn ? special Zidane-style skill ? Mike Tyson skill ? Hai mảngcác hành động ấy phải đem ra mà chọi lẫn nhau. Bởi thế mang tiếng là “Advance C++” nhưng thực ra trong lập trình game vẫn chỉlà “newbie”)prototype template functionChuẩn bị một tập tin tên là “array.h”CODE#ifndef ARRAY_H#define ARRAY_H#include <iostream>using namespace std;template<class T>class Array;template<typename T>bool equal(const Array<T>&,const Array<T>&);template<typename T>ostream& operator<<(ostream&,const Array<T>&);template<class T>class Array{    T* array;int size;public:    Array(int n);    ~Array();    void setValue(const T&,int n);    friend bool equal <>(const Array<T>&,const Array<T>&);    friend ostream& operator<< <>(ostream&,const Array<T>&);};#include "array.cpp"#endifChuẩn bị một tập tin tên là “array.cpp”CODEtemplate<typename T>Array<T>::Array(int n){    size=n;    array = new T[size];}template<typename T>Array<T>::~Array(){    delete [] array;}template<typename T>void Array<T>::setValue(const T& value,int n){    *(array+n) = value;}template<typename T>bool equal(const Array<T>& a1,const Array<T>& a2){    return a1.size==a2.size;}template<typename T>ostream& operator<<(ostream& os,const Array<T>& a){    for(int i=0;i<a.size;++i) os<<*(a.array+i);    return os;}
  • 22. Cuối cùng là “main.cpp”CODE#include "array.h"class Person{    int age;public:    Person()    {        age=0;    }    Person(int age)    {        this->age=age;    }    int getAge() const    {        return age;    }    friend bool operator!=(const Person& p1,const Person& p2)    {        return p1.getAge()!=p2.getAge();    }    friend ostream& operator<<(ostream& os,const Person& p)    {        os<<p.getAge()<<endl;        return os;    }};int main(){    Array<Person> a(3);    a.setValue(Person(24),0);    a.setValue(Person(15),1);    a.setValue(Person(5),2);    cout<<a;    Array<Person> b(3);    cout<<equal(a,b)<<endl;    return 0;}Giải thích: equal và operator<< đều là hai hàm bạn, do đó để hoạt động cần có sẵn lớp Array. Nhưng lớp Array muốn biên dịchđược phải cần có hai hàm này. Do đó ta phải khai báo prototype của hai hàm này trước. Nhưng vì đây là 2 template function,nênkhi khai báo lại prototype của chúng lần thứ hai trong một class template (ở đây là class Array) ta phải có cái kí hiệu này <> Khiđó là một prototype template function. Khi đó, thay vì tập tin cpp chứa thân hàm include tập tin header chứa nguyên mẫu củahàm, ta phải làm ngược lại. Kĩ thuật này hiểu và ứng dụng cực kì rắc rối nhưng khổ nỗi lại áp dụng rất nhiều về sau, đặc biệt khilàm các game lớn.Biên dịch lại mã này với GCCKhông bắt buộc, nhưng nên làm nếu như sau này bạn có định làm việc với game trong môi trường *nix và console. Hãy đem 3 tậptin này (array.h, array.cpp, main.cpp) và thử biên dịch bằng GCC trong Linux thử xem. Nhớ tạo makefile. Trong trường bọn tôi chủyếu làm việc bằng GCC và VI trong *nix chứ không phải Window. Việc sử dụng các bộ Visual Studio tuy không bị cấm nhưngkhông được khuyến khích. Và bài tập lẫn bài thi đều phải submit nguyên project kèm makefile để biên dịch trong môi trường *nixhết.Viết operator overload và copy constructor
  • 23. Trong phần trước ta đã xem các ví dụ dùng cách “tham chiếu mà tham chiếu đến con trỏ” Trong phần này chúng ta sẽ overloadtoán tử = và viết copy constructor cũng sử dụng lại cách này, mà không phải dùng đến prototype template functionCODE#include <iostream>#include <string>using namespace std;template<typename T>class Array{public:    int size;    T* elems;    Array(int);    Array(const Array<T>*&);    void setValue(const T&,int i);    T& getValue(int n);    Array<T>& operator=(const Array<T>*&);    friend bool operator!=(const Array<T>&,const Array<T>&);};template<typename T>Array<T>::Array(int size){    elems = new T[size];}template<typename T>void Array<T>::setValue(const T& value,int i){    *(elems+i) = value;}template<typename T>T& Array<T>::getValue(int i){    return *(elems+i);}template<typename T>Array<T>::Array(const Array<T>*& src){    size=src.size;    elems = new T[size];    for(int i=0;i<size;i++)        *(elems+i) = *(src.elems+i);}template<typename T>Array<T>& Array<T>::operator=(const Array<T>*& src){    if(*this!=src)    //to avoid self-assignment    {        size=src.size;        elems = new T[size];        for(int i=0;i<size;i++)            *(elems+i) = *(src.elems+i);    }    return *this;}template<typename T>
  • 24. bool operator!=(const Array<T>& a1,const Array<T>& a2){    if(a1.size!=a2.size) return true;    else for(int i=0;i<a1.size;i++)        if(*(a1.elems+i) == *(a2.elems+i)) return false;    return true;}int main(){    Array<string> a(2);    a.setValue("hello",0);    a.setValue("world",1);    Array<string> b(3);    b = a;    cout<<b.getValue(0)<<endl;    cout<<b.getValue(1)<<endl;    return 0;}
  • 25. BÀI 6: TEMPLATE (TIẾP THEO)Trình biên dịch và templateTrong bài trước chúng ta thấy một điều hơi là lạ, đó là file header array.h có chỉ thị #include file source array.cpp. Tại sao nhưvậy ?Khi trình biên dịch gặp template, nó kiểm tra cú pháp, nhưng không biên dịch ngay.Ví dụ nó gặp template<class T> nó không thể biên dịch vì nó không biết kiểu dữ liệu của T.Khi nó gặp instance đầu tiên của template, ví dụ template<int> nó biên dịch và chúng ta có phiên bản với kiểu dữ liệu int củatemplate.Khi nó gặp instance thứ hai của template, ví dụ template<double> nó cũng lại biên dịch và chúng ta có phiên bản thứ hai củatemplate, phiên bản với kiểu dữ liệu double. Vân vân.Thông thường chúng ta viết định nghĩa lớp và nguyên mẫu các hàm của lớp đó ở file header (đuôi .h) rồi mới viết thân cho cáchàm đó ở một file source (đuôi .cpp), mà file cpp này include luôn file header đó.Template phải làm ngược lại. Vì lí do nói trên, cả định nghĩa lớp, nguyên mẫu các hàm lẫn thân của các hàm đó của một lớptemplate phải được biên dịch cùng nhau. Do đó khi tách rời định nghĩa của một lớp template ra chứa trong một file header riêng,file header đó phải include file source chứa thân các hàm của lớp template đó, rồi một file nào khác muốn dùng template đó phảiinclude cái file header đó.Ở đây còn một phần nữa về export, tôi đã cắt đi. Có nhiều thứ sau này tôi cũng sẽ cắt đi, nhằm giảm tải cho chương trình xuốngđến mức tối thiểu nhất có thể được. Nhưng an tâm là những thứ quan trọng nhất đều có đầy đủ.Dùng từ khóa nào, class hay typenameVề cơ bản, sự khác biệt giữa chúng là không rõ ràng, cả 2 đều có cùng ý nghĩa và cùng cho kết quả như nhau, bạn muốn dùng từkhóa nào cũng được.Nhưng có lúc bạn phải dùng từ khóa typename, ví dụCODEtemplate<typename T>class Thing {    T::SubType *ptr;};Chúng ta muốn khai báo 1 con trỏ thuộc kiểu SubType của T, nhưng C++ sẽ hiểu là chúng ta muốn nhân giá trị SubType của kiểuT với ptr Lúc này chúng ta bắt buộc phải dùng từ khóa typenameCODEtemplate<typename T>class Thing{    typename T::SubType *ptr;};Chuyên môn hóa template (template specialization)Giả sử ta có một lớp templatetemplate<class T>class pair{…}Khi ta tạo một instance bằng cách khai báo cụ thể kiểu của T, ví dụ là int, tức là ta đã chuyên môn hóa (specialization) lớptemplate đópair<int> myobject(155,36);Đôi khi ta muốn lớp template tạo ra những instance cụ thể để thực hiện những công việc cụ thể riêng đối với một loại dữ liệu cụthể nào đó, ta dùng chuyên môn hóa cụ thể (explicit specialization)Trong ví dụ dưới đây ta muốn riêng đối với kiểu dữ liệu cụ thể là int thì lớp template có một hàm trả về phần dư giữa hai sốnguyên, còn với các kiểu dữ liệu khác thì nó trả về 0CODEtemplate<class T>class pair {    T value1, value2;
  • 26.     public:    pair(T first, T second) {        value1=first; value2=second;    }    T module() {return 0;}};//viết lại định nghĩa lớp chuyên môn hóa cho kiểu dữ liệu inttemplate<>class pair<int> {    int value1, value2;    public:    pair(int first, int second) {        value1=first; value2=second;    }    int module ();};//hàm module dành riêng cho lớp chuyên môn hóatemplate<>int pair<int>::module() {    return value1%value2;}int main() {    pair<int> myints(100,75);    cout<<myints.module()<<endl;    pair<float> myfloats(100.0,75.0);    cout<<myfloats.module()<<endl;    return 0;}Ép kiểu dữ liệu (casting) trong C++Trong C chúng ta ép kiểu dữ liệu như sauint n=(int)45.87;Trong C++ có 1 cách ép kiểu dữ liệu như sauint i = static_cast<int>(45.87);Cho ra kết quả như nhau (tạm chỉ cần biết thế)Chúng ta sẽ còn quay trở lại với casting trong C++ sauDiễn dịch đối số (argument deduction)Xem lại hàm template dưới đâytemplate <typename T> T max(T a, T b)Kiểu dữ liệu của 2 đối số (argument) a và b sẽ được quyết định bởi kiểu dữ liệu của 2 tham số (parameter) truyền vào hàm này.Và 2 đối số này cùng là kiểu T, nên 2 tham số này phải cùng một kiểu. C++ không có tự động chuyển kiểu ở đây. Ví dụmax(7, 5); //hợp lệ, T lúc này là kiểu int, 2 tham số cùng kiểu intmax(7, 5.2); //không hợp lệ, T lúc này là kiểu int (kiểu dữ liệu của tham số được truyền trước tiên, nhưng 2 tham số thì một cáikiểu int, một cái kiểu doubleCó 2 cách xử lí chuyện nàyCách 1: casting (ép kiểu) tham số đầu tiênmax(static_cast<double>(7), 5.2); //lúc này T là kiểu double, 2 đối số đều cùng kiểu doubleCách 2: explicit specialization (chuyên môn hóa cụ thể) cho T thành doublemax<double> (7, 5.2);Đối số của template (template argument)template thường có các đối số là typename T (với T là kiểu dữ liệu chưa biết) Nhưng thực ra template cũng có các đối số là các kiểudữ liệu đã biết
  • 27. Đối số kiểu primitive, ví dụ kiểu intCODEtemplate<typename T,int size>class Array{    T* array;public:    Array();};template<typename T,int size>Array<T,size>::Array(){    array = new T[size];}int main(){    Array<string,5> a;    return 0;}Đối số là một lớp template khácCODE#include <iostream>#include <string>using namespace std;template<typename T>class Array{    T* array;public:    Array();};template<typename T>Array<T>::Array(){    array = new T;}template<typename T,typename U = Array<typename V>,int size>class Stack{    U* elems;public:    Stack();};template<typename T,typename U = Array<typename V>,int size>Stack<T,U,size>::Stack(){    elems = new U[size];}int main(){    Stack<string,Array<double>,5> a;    return 0;}Còn mấy phần nữa, nhưng rất cao và ít dùng về sau trong lập trình game, mà chủ yếu cho lập trình bậc thấp, phần cứng, hệ điềuhành, nên tôi bỏ, như thế này đủ nhức đầu và khó nhớ rồi. Các bác học xong template rồi đó, nắm rõ tất cả các kĩ thuật vềtemplate để chuẩn bị cho học STL về sau.
  • 28. Làm cái bài tập chứ nhỉ. Đề đơn giản thôi: lập trình một danh sách liên kết đơn dùng template, đủ các phép thêm, xóa, sửa, truyxuất. Có sẵn cái chương trình mẫu ở dưới này. Chương trình này cực yếu, không có xóa, hủy … Chương trình cần các bác bổ sungđó.CODEtemplate<typename T>class Node{    T data;Node<T>* next;public:    Node<T>(T data){(*this).data=data;(*this).next=0;}    T getData(){return data;}    void setData(T data){(*this).data=data;}    Node<T>* getNext(){return next;}    void setNext(Node<T>* next){(*this).next=next;}};template<typename T>class List{    Node<T>* front;Node<T>* rear;Node<T>* current;public:    List<T>(){(*this).front=(*this).rear=(*this).current=0;}    List<T>(const List<T>& l()){        (*this).front=(*this).rear=(*this).current=0;        Node<T>* temp=new (nothrow)Node<T>;if(temp==0) exit(1);        temp=l.front;        while(temp!=0){            insertRear(temp->getData());            temp = temp->getNext();        }    }    ~List<T>(){        Node<T>* temp=new (nothrow)Node<T>;if(temp==0) exit(1);        while(front!=0){            temp=front;            front=(*front).next;            delete temp;        }    }    void insertFront(T data){        Node<T>* temp=new (nothrow)Node<T>;if(temp==0) exit(1);        (*temp)->setData(data);        if(front==0) rear=temp;        else temp->setNext(front);        front=temp;    }    void insertRear(T data){        Node<T>* temp=new (nothrow)Node<T>;if(temp==0) exit(1);        (*temp)->setData(data);        if(rear==0) front=temp;        else rear->setNext(temp);        rear=temp;    }    void reset(){if(front!=0) current=front;}    void next(){if(current!=0) current = current->getNext();}    T getCurrentData(){if(current!=0) return current->getData();}    bool endOfList(){return(current==0);}
  • 29. };
  • 30. BÀI 7: CONST, STATIC, EXCEPTION, CASTING (BIẾN HẰNG, BIẾN TĨNH, NGOẠI LỆ, ÉP KIỂU)CONSTconst int p và int const p là như nhauint* const p nghĩa là một hằng số loại con trỏ mà trỏ đến một biến số kiểu nguyên, nghĩa là bạn không thể thay đổi để con trỏ nàytrỏ đến một nơi khác được nữaCODEint a = 3;int b = 5;int* const p = &a;p = &b;    //không hợp lệconst int* p nghĩa là một biến số loại con trỏ mà trỏ đến một hằng số kiểu nguyên, nghĩa là bạn có thể thay đổi để con trỏ này trỏđến một nơi khác, nhưng không thể thay đổi dữ liệu ở nơi nó trỏ đếnCODEint a = 3;int b = 5;const int* p = &a;p = &b;    //hợp lệ*p = 6;    //không hợp lệconst int *& p nghĩa là một tham chiếu mà tham chiếu tới một biến số loại con trỏ mà trỏ đến một hằng số kiểu nguyênint const *& p nghĩa là một tham chiếu mà tham chiếu tới một hằng số loại con trỏ mà trỏ đến một biến số kiểu nguyênMột biến số const nghĩa là biến đó phải được gán giá trị ngay lúc khai báo và giá trị ấy vĩnh viễn không thay đổi (cái này quá phổbiến rồi)Một hàm số const nghĩa là hàm số đó sẽ không thay đổi giá trị của bất cứ tham số nào của nó. Một hàm số phải được khai báoconst nếu nó có ít nhất một tham số constCODEclass Tree{    int age;public:int getAge() const{return age;}    friend ostream& operator<<(ostream& os,const Tree& t)    {os<<t.getAge();return os;}};Hàm số getAge phải là một hàm số const để bất kì hàm số khác dùng tham số const Tree có thể sử dụng hàm số getAgeHàm số trả về constVí dụ chúng ta viết một hàm như sauCODEint& hello(){    int temp=5;    return temp;}Và một ai đó có thể lợi dụng để gán giá trị cho nó, rất nguy hiểmCODE
  • 31. hello() = 3;Nên chúng ta phải thay đổi để nó trả về constCODEconst int& hello(){    int temp=5;    return temp;}Bảo vệ tham sốHẳn bạn còn nhớ ví dụ này chứCODEvoid add10(int *a){    *a=*a+10;}add10(&n);Ý nghĩa ở đây là, một hàm hoàn toàn có thể thay đổi giá trị của tham số nếu ta truyền vào nó địa chỉ của biến đó. Mà việc truyềnđịa chỉ là việc phải làm. Vì đôi khi không thể không truyền địa chỉ, ví dụ kiểu mảngCODEvoid dosomething(char* a){...}char* m = "hello";dosomething(m);Khi đó ta dùng từ khóa const để ngăn việc hàm thay đổi giá trị của đối sốCODEvoid dosomething(const char* a){...}char* m = "hello";dosomething(m);STATICMột biến số const nghĩa là biến đó phải được gán giá trị ngay lúc khai báo và giá trị ấy vĩnh viễn không thay đổiMột biến số static nghĩa là biến đó phải được gán giá trị ngay trước khi tạo một instance của một lớp và giá trị ấy được thay đổi,nhưng chỉ có duy nhất một biến static ấy tồn tại đối với tất cả instance của lớp đóCODEclass MyClass{public:static int i;};int MyClass::i;    //tồn tại độc lập, không phụ thuộc instanceint main(){    MyClass::i=30;    MyClass x,y;    x.i=26;cout<<y.i<<endl;}Một hàm số static nghĩa là hàm số ấy có thể độc hoạt động lập mà không cần tạo đối tượng của lớp
  • 32. CODEclass MyClass{public:static void print(char* s){cout<<s<<endl;}};int main(){    MyClass::p("Hello");    return 0;}Ngoại lệ (Exception)Hàm dưới đây có thể ném ra bất cứ ngoại lệ nàoCODEvoid in(int a);    //chỉ cần khai báo bình thườngHàm dưới đây không thể ném ra bất cứ ngoại lệ nàoCODEvoid in(int a) throw();Không như Java, trong C++ bất cứ lớp nào cũng có thể là lớp ngoại lệ (exception class), và cũng không cần phải là lớp con của lớpnào hếtChúng ta bắt ngoại lệ bằng try catch như JavaCODEclass DivideByZero{public:    void msg()    {        cout<<"Can not divide by zero"<<endl;    }};int divide(int a,int b) throw(DivideByZero){    if(b==0) throw DivideByZero();    return a/b;}int main(){    try    {        int a=divide(3,0);    }    catch(DivideByZero e)    {        e.msg();    }    return 0;}
  • 33. Khi chúng ta bắt được một ngoại lệ, chúng ta có thể ném nó ra để bắt lại một lần nữa bằng throw;CODEclass DivideByZero{public:    void msg()    {        cout<<"Can not divide by zero"<<endl;    }};int divide(int a,int b) throw(DivideByZero){    if(b==0) throw DivideByZero();    return a/b;}int main(){    try    {        try        {            int a=divide(3,0);        }        catch(DivideByZero e)        {            e.msg();throw;    //ném ra để bắt lại lần hai        }    }    catch(DivideByZero)    {        cout<<"Stopped! Thats enough"<<endl;    }    return 0;}Chúng ta có thể ném ra và bắt lại bất cứ cái gì chứ không phải chỉ một lớpCODEint main(){    int s[5];    try    {        for(int i=0;i<6;i++)        {            if(i>=5) throw "Out of range";            s[i] = i;        }    }    catch(char* c)    {        cout<<c<<endl;    }    return 0;
  • 34. }Chúng ta có thể ném ra nhiều thứ bằng một chỉ thị try và bắt lại tất cả mọi thứ, mỗi thứ một chỉ thị catch khác nhauCODEint main(){    try    {        int size;cin>>size;        if(size>10) throw "So big";        else throw size;    }    catch(int n)    {        cout<<n<<" is not here"<<endl;    }    catch(char* c)    {        cout<<c<<endl;    }    return 0;}Nếu có ngoại lệ nào không bị bắt, hàm đặc biệt void terminate() sẽ được gọi ngay lập tức để chấm dứt chương trìnhChúng ta có thể bắt tất cả các loại ngoại lệ để xử lí chỉ với một chỉ thị catch(...)CODEint main(){    try    {        int size;cin>>size;        if(size>10) throw "So big";        else throw size;    }    catch(...)    {        cout<<"throw exception"<<endl;    }    return 0;}Ép kiểu (Casting)Ép kiểu theo cách của Cint n=(int)45.87;static_cast (ép kiểu tĩnh)Kiểm tra lúc biên dịch (compile time)int n=static_cast<int>(45.87);int n=static_cast<int>(“hello”); //lúc biên dịch sẽ phát sinh lỗidynamic_cast (ép kiểu động)Kiểm tra lúc thực thi (runtime) Lớp dùng với dynamic_cast phải có hàm ảo.downcastÉp kiểu các con trỏ và các đối tượng từ kiểu lớp cha thành kiểu lớp con, được gọi là downcast. Đây là một kiểu casting quan trọng,dùng nhiều về sau trong RTTI. Có 2 cách thực hiện downcastbằng static_cast
  • 35. CODEclass Base{};class Derived : public Base{};Base* b=new Derived;Derived* d = static_cast<Derived*>(b);bằng dynamic_castCODEclass Base{virtual void func(){}};class Derived : public Base{};Base* b = new Derived;Derived* d = dynamic_cast<Derived*>(b);upcastÉp kiểu các con trỏ và các đối tượng từ kiểu lớp con thành kiểu lớp cha, được gọi là upcast. upcast hiếm dùng hơn downcast. Có 2cách thực hiện upcastbằng static_castCODEclass Base{};class Derived : public Base{};Derived* d = new Derived;Base* b = static_cast<Base*>(d);bằng dynamic_castCODEclass Base{virtual void func(){}};class Derived : public Base{};Derived* d = new Derived;Base* b = dynamic_cast<Base*>(d);const_cast (ép kiểu hằng)const_cast dùng chủ yếu với các con trỏconst_cast dùng để thay đổi một biến số thành một hằng số (thêm từ khóa const vào)CODEint a=3;int* b=&a;*b = 8;//hợp lệconst int* c=const_cast<int*>(b);*c=5;    //không hợp lệcout<<*c;cout<<a;const_cast dùng để thay đổi một hằng số thành một biến số (gỡ từ khóa const ra)CODEconst int a=3;const int* b=&a;*b = 8;    //không hợp lệint* c=const_cast<int*>(b);*c=5;    //hợp lệcout<<*c;cout<<a;
  • 36. reinterpret_cast (ép kiểu thông dịch lại)reinterpret_cast sẽ ép kiểu bất cứ con trỏ hay đối tượng nào mà không hề có sự kiểm tra nào cả. Không khuyến khích dùng và bâygiờ ta cũng chưa phải dùng, sẽ học sau.
  • 37. BÀI 8: STL - SEQUENTIAL CONTAINERYêu cầu: học xong môn cấp trúc dữ liệu và giải thuật cơ bản hoặc tương đương để có kiến thức cơ bản về các cấu trúc dữ liệu độngnhư danh sách liên kết (linked list), hàng đợi (queue), ngăn xếp (stack), tập hợp (set), ánh xạ (map) và các giải thuật tìm kiếm,sắp xếp cơ bản.STL (Standard Template Library) là một bộ thư viện vô cùng hữu dụng của C++ dùng để làm việc với các cấu trúc dữ liệu phổ biếnnhư danh sách, hàng đợi, ngăn xếp và các phép toán chủ yếu với các cấu trúc dữ liệu này như tìm kiếm, sắp xếp, truy xuất, thêm,xóa, sửa.STL bao gồm*Các container (các bộ lưu trữ dữ liệu) là các cấu trúc dữ liệu phổ biến đã template hóa dùng để lưu trữ các kiểu dữ liệu khácnhau. Các container chia làm 2 loại:-sequential container (các bộ lưu trữ dữ liệu tuần tự) bao gồm list, vector và deque-associative container (các bộ lưu trữ dữ liệu liên kết) bao gồm map, multimap, set và multiset*Các iterator (các con trỏ dữ liệu) là các con trỏ để trỏ đến các phần tử trong các bộ lưu trữ*Các algorithm (các thuật toán lưu trữ dữ liệu) là các hàm phổ biến để làm việc với các bộ lưu trữ như thêm, xóa, sửa, truy xuất,tìm kiếm, sắp xếp*Các function object (các đối tượng hàm) là các hàm và phép toán phổ biến để làm việc với các phần tử được lưu trữ cũng như cácbộ lưu trữ và các thuật toán lưu trữ như cộng, trừ, nhân, chia, so sánh*Các adapter (các bộ tương thích) Các adapter chia làm 3 loại-container adapter (các bộ tương thích lưu trữ) bao gồm stack, queue và priority_queue-iterator adapter (các bộ tương thích con trỏ)-function adapter (các bộ tương thích hàm)Trước tiên ta học về các container.LISTCODE#include <list>list rong STL là danh sách liên kết đôi, không hỗ trợ random access (truy xuất dữ liệu bất kì) Nghĩa là nếu bạn muốn truy xuất mộtphần tử bất kì trong list thì bạn phải truy xuất từ phần tử đầu tiên hoặc phần tử cuối cùng của list rồi truy xuất dần đến phần tử đóKhởi tạo sao chéplist có thể khởi tạo sao chép từ mảng, từ list khác hoặc từ các container khácCODEint a[10];list<int> list1(a+2,a+7);list<int> list2(list1.begin()++,--list1.end());Các hàm thường dùng của listCODEvoid push_front(T element): đưa một phần tử vào đầu listvoid push_end(T element): đưa một phần tử vào cuối listvoid pop_front(): gỡ phần tử đầu list ravoid pop_end(): gỡ phần tử cuối list raiterator begin(): trả về iterator trỏ đến phần tử đầu listiterator end(): trả về iterator trỏ đến phần tử cuối listVi dụ dưới chúng ta tạo một list, đưa phần tử vào và truy xuất phần tử
  • 38. CODElist<string> list1;list1.push_back("Zebra");list1.push_back("Penguin");list1.push_front("Lion");list<string>::iterator i;for(i=list1.begin();i!=list1.end();++i) cout<<*i<<endl;Toán tử * được dùng để lấy giá trị của iterator*i: trả về giá trị được trỏ tới bởi iterator i, ở đây là các phần tử của list1Nếu chúng ta muốn một list chứa nhiều list, ta chỉ cần khai báoCODElist<list<string> > listOfList;Nếu list có khai báo const, chúng ta phải dùng const_iterator thay vì iteratorCODEconst list<string> list1;list<string>::const_iterator i = list1.begin();Các hàm thường dùng khác của listCODEint n=list1.size();//trả về số phần tử của listbool b=list1.empty();//kiểm tra list, nếu rỗng (không có phần tử) thì trả về true, ngược lại trả về falselist1.insert(list1.begin(),"Seadog");//chèn phần tử "Seagon" vào vị trí đầu listlist1.insert(++list1.begin(),2,"Seadog");//chèn phần tử "Seagon" vào một vị trí cụ thểlist1.erase(list1.begin());//xóa một phần tử ở một vị trí cụ thểlist1.erase(++list1.begin(),3);//xóa 3 phần tử bắt đầu từ một vị trí cụ thểlist1.clear();//xóa tất cả các phần tửlist1.remove("Zebra");//tìm kiếm và xóa phần tử "Zebra"list1.sort();//sắp xếp tăng dần (ascending)list1.reverse();//sắp xếp giảm dần (descending)list1.resize(int);//thiết lập số phần tử mới của listiterator i=list1.find(++list1.begin(),--list1.end(),"Penguin");//tìm kiếm phần tử "Penguin", bắt đầu ở một vịtrí cụ thể kết thúc ở một vị trí cụ thể khác, trả về iterator trỏ đến phần tử này. Nếu không tìm thấy, hàmnày trả về vị trí kết thúc, ở đây là --list1.end()Các hàm với hai listCODElist<string> list1;list<string> list2;list1.splice(--list1.end(),list2,list2.begin());//splice(cut và paste) một phần tử từ vị trí list2.begin() của list2 đến vị trí --list1.end() của list1list1.splice(--list1.end(),list2);//splice(cut và paste) tất cả phần tử của list2 đến vị trí --list1.end() của list1list1.merge(list2);//merge 2 list, nghĩa là list1 = list1 + list2;list2.swap(list1);//swap 2 list, nghĩa là temp = list2;list2 = list1;list1 = temp;VECTORCODE
  • 39. #include <vector>vector giống list ngoại trừ-cho phép random access, với operator[], nghĩa là v[5], v[6], etc như mảng-được tối ưu hóa với các phép toán ở phía đuôi (rear operations)-không có sẵn các hàm push_front,pop_front,splice,sort,merge,reserveCODEvector<int> v;v.push_back(5);cout<<v[0];DEQUECODE#include <deque>deque giống list ngoại trừ-cho phép random access, với operator[], nghĩa là d[5], d[6], etc như mảng-được tối ưu hóa với các phép toán ở phía đầu (front operations)-không có sẵn các hàm splice,sort,merge,reserveCODEdeque<int> d;d.push_front(5);cout<<d[0];ALGORITHMCODE#include <algorithm>Do vector và deque không có sẵn các hàm splice,sort,merge,reserve nên nếu ta muốn sử dụng các chức năng này với vector vàdeque ta phải dùng các hàm trong thư viện algorithmSắp xếpCODE#include <algorithm>vector<int> v;sort(v.begin(),v.end());//sắp xếp tăng dần (ascending)reverse(v.begin(),v.end());//sắp xếp giảm dần (descending)Sao chépCODEint a[]={1,2,3,4};vector<int> v;v.resize(3);copy (a,a+3,v.begin());//sao chép 3 phần tử từ mảng a vào vector vMerge và swapCODE
  • 40. vector<int> v, v2;merge(v1.begin(),v1.begin()+5,++v2.begin(),v2.end(),v1.begin());//hợp 2 phần dữ liệu, phần một từ v1.begin()đến v1.begin()+5, phần hai từ ++v2.begin() đến v2.end(), sau đó chép tất cả vào v1 bắt đầu từ v1.begin()swap(v1, v2);CÁC PHẦN TỬ LÀ CON TRỎGiả sử ta có lớp Person với hàm khởi tạo Person(char* name)CODEĐể đưa một phần tử Person vào vector:vector<Person> v;v.push_back(Person("C"));Để đưa một phần tử con trỏ Person vào vector, ta phải dùng new để cấp phát bộ nhớ:vector<Person*> vp;vp.push_back(new Person("M"));Để truy xuất phần tửvector<Person>::iterator i;for(i=v.begin();i!=v.end();++i) cout<<*i<<endl;Để truy xuất phần tử con trỏvector<Person*>::iterator ip;for(ip=vp.begin();ip!=vp.end();++ip) cout<<**ip<<endl;*i: trả về giá trị được trỏ tới bởi iterator i, ở đây là các phần tử của vector vvector<Person>::iterator i; (*i) trả về Person**ip: trả về giá trị của các con trỏ được trỏ tới bởi iterator i, ở đây là các phần tử con trỏ của vector vpvector<Person*>::iterator ip; (*ip) trả về Person*như vậy (**ip) trả về *(Person*)
  • 41. BÀI 9:FUNCTION OBJECT (ĐỐI TƯỢNG HÀM)Function objectMột function object (đối tượng hàm) là một object (đối tượng) được sử dụng như một function (hàm). Một Một function object làmột instance của một lớp mà lớp đó phải có ít nhất một hàm thỏa-quyền truy xuất phải là public-phải là một hàm thành viên, không phải là một hàm friend-không phải là một hàm static-có khai báo operator()Ví dụ ta viết một hàm bình thường như sauCODEvoid iprintf(int i) const{    cout<<i<<endl;}Bây giờ ta sẽ viết một lớp như sauCODEclass iprintf{public:    void operator()(int i) const    {        cout<<i<<endl;    }};Instance của lớp này là một object được gọi là function object, là một object được sử dụng như một function. Sử dụng như thếnào ?CODEiprintf x;x(5);hoặcCODEiprintf()(5);Khi ta gọi iprintf()(5) nghĩa là chúng ta đang gọi đến operator() của lớp iprintffunction object còn được gọi là một functor hay một functional. Từ đây khi đề cập đến function object sẽ dùng functor.Ví dụ dưới đây là một lớp có nhiều hơn một operator()CODEclass iprintf{int i;    public:iprintf(int i):i(i){}public:    void operator()() const    {        cout<<i<<endl;    }    void operator()(int i) consthttp://river.congdongso.com/advc++/bai9.htm
  • 42.     {        cout<<"Integer:"<<i<<endl;    }    void operator()(float f) const    {        cout<<"Float:"<<f<<endl;    }};int main(int argc,char** argv){    iprintf x(20);    x();    x(5);    //giả sử không có operator()(int i), câu này sẽ gọi operator()(float f)    x(2.3);    //giả sử không có operator()(float f), câu này sẽ gọi operator()(int i) với i = 2    x("something");    //lỗi    return 0;}Tương tự thay vì iprintf(5); x(7); chúng ta cũng có thể gọi iprintf(5)(7);Có một điều chú ý ở ví dụ trên là nếu cùng tồn tại operator()(int i) và operator()(float f) thì câu lệnh x(2.3); sẽ báo lỗi ambiguous(nhập nhằng) giữa hai hàm. Có một cách đơn giản là viết lại thành x((float)2.3);PredicatePredicate có một định nghĩa khác phức tạp hơn. Ở đây chỉ nêu điều cần thiết nhất có liên can đến chương trình.Một predicate được đề cập đến ở đây là một function hoặc một functor có điều kiện giá trị trả về đúng hoặc sai hoặc một giá trị cóthể chuyển kiểu thành đúng hoặc sai. Trong C/C++, đúng có nghĩa là khác 0 và sai có nghĩa là bằng 0Ví dụ hàm sau đây là một predicateCODEdouble truefalse(double n){    return n;}Một số hàm thường dùng trong algorithmHàm findCODEvector<int> v;v.push_back(4);v.push_back(3);v.push_back(2);vector<int>::iterator i = find (v.begin(),v.end(),3);if(i!=v.end()) cout<<*i;Hàm find tìm từ phần tử v.begin() đến phần tử v.end() và trả về iterator trỏ đến phần tử có giá trị là 3, nếu không tìm thấy sẽ trảvề v.end()Hàm find_ifCODEint IsOdd(int n){    return n%2;}int main(){    list<int> l;    l.push_back(4);l.push_back(5);l.push_back(2);http://river.congdongso.com/advc++/bai9.htm
  • 43.     list<int>::iterator i=find_if(l.begin(),l.end(),IsOdd);    if(i!=l.end()) cout<<*i;}Hàm find_if tìm từ phần tử v.begin() đến phần tử v.end() và trả về iterator trỏ đến phần tử có giá trị thỏa predicate, nếu không tìmthấy sẽ trả về v.end()Lưu ý, lúc này IsOdd đóng vai trò là một predicate, xác định xem phần tử của list có là số lẻ hay không (tức là khi đưa vào làmtham số của hàm IsOdd có trả về một số khác 0 hay không)Chúng ta viết lại predicate này bằng cách dùng functorCODEclass IsOdd{public:    bool operator()(int n) const    {        return n%2;    }};int main(){    list<int> l;    l.push_back(4);l.push_back(5);l.push_back(2);    list<int>::iterator i=find_if(l.begin(),l.end(),IsOdd());    if(i!=l.end()) cout<<*i;}Hàm equalỞ trên chúng ta mới xét các ví dụ với predicate có một đối số, ta xét một hàm khác của algorithm dùng predicate nhiều hơn mộtđối số, hàm equalCODEclass compare{public:    bool operator()(int i,int j) const    {        return i==j;    }};int main(){    compare c;    int a[] = {1, 2, 3, 4, 5};    list<int> l(a,a+3);    //list ít phần tử hơn mảng    cout<<equal(l.begin(),l.end(),a,c)<<endl;    a[2] = 6;    cout<<equal(l.begin(),l.end(),a,c)<<endl;    return 0;}Hàm equal so sánh từng phần tử của list từ phần tử l.begin() đến phần tử l.end() với từng phần tử tương ứng của mảng a sao chomỗi cặp phần tử đều thỏa predicate là c, trả về là true nếu từng cặp phần tử so sánh với nhau đều cho giá trị true (không cần quantâm đến số lượng phần tử có tương ứng không) Nhưng chỉ cần một cặp trả về false thì hàm sẽ trả về falsehttp://river.congdongso.com/advc++/bai9.htm
  • 44. Hàm searchHàm search tìm vị trí của một chuỗi con trong một chuỗi lớn hơn, nếu tìm thấy thì trả về iterator trỏ đến vị trí của chuỗi con đótrong chuỗi lớn. Hàm này có hai “phiên bản”CODEint a[] = {3,4,5};vector<int> v;for(int i = 0;i<10;i++) v.push_back(i);vector<int>::iterator iv = search(v.begin(),--v.end(),a,a+2);if(iv!=--v.end()) cout<<iv-v.begin();Phiên bản thứ nhất tìm vị trí của chuỗi con từ phần tử có vị trí a+0 đến phần tử có vị trí a+2 trong chuỗi lớn hơn từ phần tử có vịtrí v.begin() đến phần tử có vị trí --v.end() Nếu không tìm thấy thì trả về vị trí --v.end()Trong đoạn mã trên có một điều đáng chú ý là iv-v.begin Lưu ý là hàm search trả về một iterator, iterator này là iv = v.begin() +vị trí của chuỗi con. Do đó đơn giản vị trí của chuỗi con = iv-v.begin()CODEclass compare{public:    bool operator()(int i,int j) const    {        return i==j+1;    }};int main(){    int a[] = {3,4,5};    vector<int> v;    for(int i = 0;i<10;i++) v.push_back(i);    vector<int>::iterator iv = search(v.begin(),v.end(),a,a+2,compare());    if(iv!=v.end()) cout<<iv-v.begin();    return 0;}Phiên bản thứ hai sẽ phải cần đến một predicate có hai đối số giống như hàm equal. Phiên bản thứ hai tìm vị trí của chuỗi con từphần tử có vị trí a+0 đến phần tử có vị trí a+2 trong chuỗi lớn hơn từ phần tử có vị trí v.begin() đến phần tử có vị trí --v.end() saocho từng cặp phần tử thỏa compare() (ở đây là điều kiện phần tử của v = phần tử của a + 1). Nếu không tìm thấy thì trả về vị trí --v.end()Tương tự như hàm search này là hàm find_end, nhưng thay vì trả về vị trí đầu tiên của chuỗi con xuất hiện trong chuỗi lớn thì lạitrả về vị trí cuối cùng của chuỗi con xuất hiện trong chuỗi lớn.Hàm for_eachHàm for_each dùng để duyệt từng phần tử trong một chuỗi các phần tử cho trướcDùng for_each để in ra các phần tử, ví dụCODEvoid display(const string& s){cout<<s<<endl;}list<string> l;l.push_back("hello");l.push_back("world");for_each(l.begin(),l.end(),display);Tương tự dùng với một functorCODEtemplate<typename T>class Outputhttp://river.congdongso.com/advc++/bai9.htm
  • 45. {public:    void operator()(const T& t){cout<<t<<endl;}};int main(int argc, char* argv[]){    list<string> l;l.push_back("hello");l.push_back("world");    for_each(l.begin(),l.end(),Output<string>());}Hàm countHàm count dùng để đếm số lượng phần tử trong một chuỗi các phần tử cho trướcCODElist<string> l;l.push_back("hello");l.push_back("world");cout<<(int)count(l.begin(),l.end(),"hello")<<endl;Hàm count_ifHàm count_if dùng để đếm số lượng phần tử thỏa một điều kiện nào đó trong một chuỗi các phần tử cho trước, hàm cần mộtpredicate một đối sốCODEclass IsOdd{public:bool operator()(int n) const{return (n%2)==1;}};int main(int argc, char* argv[]){    list<int> l;for(int i=0;i<10;i++) l.push_back(i);    cout<<(int)count_if(l.begin(),l.end(),IsOdd())<<endl;}Toàn bài chúng ta học về predicate và thư viện algorithm, còn một số hàm sẽ học sau. Có một điều đáng lưu ý đó là predicate mộtđối số và hai đối số. Số lượng đối số của predicate được gọi là hạng (arity) của predicate. Biết sơ điều này và chuẩn bị cho bài tiếptheo.http://river.congdongso.com/advc++/bai9.htm
  • 46. BÀI 10:THƯ VIỆN FUNCTIONALCODE#include <functional>Hạng của một predicateCó nhiều sự mập mờ do từ đồng nghĩa giữa các khái niệm toán học trong cả hai ngôn ngữ tiếng Việt và tiếng Anh, do đó địnhnghĩa sau chỉ ở mức cố gắng chính xác nhất có thể được:Số toán tử (operand) của một phép toán (operator), tương ứng là số đối số (argument) của một hàm (function), được gọi là hạng(arity) của phép toán hay hàm đóTương tự, số toán tử (operand) của một biểu thức (expression), tương ứng là số đối số (argument) của một đối tượng hàm(functor), được gọi là hạng (arity) của biểu thức hay đối tượng hàm đóVí dụUnary (đơn nguyên, đơn phân, một toán hạng, một ngôi)n! (giai thừa của n) là một unary operatorn! là một unary expression, chỉ bao gồm một unary operatorint giaithua(int n) là một unary functionmột object của class giaithua{int operator()(int n)…} là một unary functorBinary (nhị nguyên, nhị phân, hai toán hạng, hai ngôi)a + b là một binary expression, chỉ bao gồm một binary operatorint addition(int a,int b) là một binary functionmột object của class addition{int operator()(int a,int b)…} là một binary functorTernary (tam nguyên, tam phân, ba toán hạng, ba ngôi)b * b – 4 * a * c là một ternary expression, bao gồm một unary operator và ba binary operatordouble delta(double a, double b,double c) là một ternary functionmột object của class delta{ double operator()(double a, double b,double c)…} là một ternary functorn-ary (đa nguyên, đa phân, nhiều toán hạng, nhiều ngôi)Tương tự như trên, ngoài ra còn có nhiều từ gốc Latin khác như quaternary (bốn toán hạng) quinary (năm toán hạng) … gọi chunglà nhiều toán hạng.Hạng của predicate tức là hạng của function hay functor mà đóng vai trò predicate. Như ví dụ ở trên, addition là một binarypredicate, delta là một ternary predicateCấu trúc unary_function trong thư viện functionalTrong thư viện functional đã định nghĩa sẵn cấu trúc unary_functionCODEtemplate<class Arg,class Result>struct unary_function{    typedef Arg argument_type;    typedef Result result_type;};unary_function là cấu trúc định nghĩa sẵn cho tất cả unary function và unary functor với Arg là kiểu dữ liệu của đối số và Result làkiểu trả về của hàm có operator()Chúng ta viết lại lớp IsOdd, định nghĩa nó là một unary_functionCODEclass IsOdd:public unary_function<int,bool>{public:    bool operator()(int n) const    {        return n%2;    }};
  • 47. Cấu trúc binary_function trong thư viện functionalTương tự, trong thư viện functional đã định nghĩa sẵn cấu trúc binary_functionCODEtemplate<class Arg1,class Arg2,class Result>struct binary_function{    typedef Arg1 first_argument_type;    typedef Arg2 second_argument_type;    typedef Result result_type;};binary_function là cấu trúc định nghĩa sẵn cho tất cả binary function và binary functor với Arg1 là kiểu dữ liệu của đối số thứ nhấtvà Arg2 là kiểu dữ liệu của đối số thứ hai và Result là kiểu trả về của hàm có operator()Chúng ta viết lại lớp compare, định nghĩa nó là một binary_functionCODEclass compare:public binary_function<int,int,bool>{public:    bool operator()(int i,int j) const    {        return i==j;    }};Tương tự chúng ta có thể tự viết các cấu trúc ternary_function, quaternary_function, vân vân nếu muốn. Ví dụ dưới đây là một cấutrúc ternary_function tự viết và một lớp được định nghĩa là một ternary_functionCODEtemplate<class Arg1,class Arg2,class Arg3,class Result>struct ternary_function{    typedef Arg1 first_argument_type;    typedef Arg2 second_argument_type;    typedef Arg3 third_argument_type;    typedef Result result_type;};class multiply:public ternary_function<int,float,long,double>{public:    double operator()(int i,float f,long l) const    {        return i*f*l;    }};Ràng buộc (bind) toán hạng cho predicateCó nhiều hàm chỉ chấp nhận một đối số, nhưng chúng ta lại cần chuyền vào cho nó các predicate là binary predicate như binaryfunction hay binary functor. Trong trường hợp đó chúng ta cần ràng buộc toán hạng cho binary predicate đó để nó trở thành mộtunary predicateVí dụ chúng ta cần dùng hàm find_if để tìm các phần tử trong một vector thỏa một binary predicate, nhưng find_if lại chỉ chấpnhận unary predicate, khi đó chúng ta cần ràng buộc toán hạng cho binary predicate đó để nó trở thành một unary predicatebinary predicate muốn được ràng buộc toán hạng phải được định nghĩa là một binary_functionHàm bind1stHàm bind1st ràng buộc toán hạng thứ nhất của một binary predicate với một giá trị cho trước để nó trở thành một unary predicate
  • 48. với đối số còn lại của binary predicate ban đầu trở thành đối số của unary predicate kết quảCODEclass compare:public binary_function<int,int,bool>{public:    bool operator()(int i,int j) const    {        return i+1==j;    }};int main(){    vector<int> v;    v.push_back(4);v.push_back(0);v.push_back(1);    vector<int>::iterator i=find_if(v.begin(),v.end(),bind1st(compare(),0));    if(i!=v.end()) cout<<i-v.begin();    return 0;}Trong ví dụ trên, đối số thứ nhất của compare() đã được ràng buộc bằng 0, compare() trở thành một predicate chỉ có một đối số làđối số còn lại của compare() ban đầu, và find_if chỉ việc truyền tham số là iterator trỏ đến các phần tử của v vào đối số này, quátrình chạy vòng lặp diễn ra giống như saucompare()(0,4) //phép so sánh 0 + 1 == 4 trả về falsecompare()(0,0) //phép so sánh 0 + 1 == 0 trả về falsecompare()(0,1) //phép so sánh 0 + 1 == 1 trả về trueHàm bind2ndHàm bind2nd ràng buộc toán hạng thứ hai của một binary predicate với một giá trị cho trước để nó trở thành một unary predicatevới đối số còn lại của binary predicate ban đầu trở thành đối số của unary predicate kết quảCODEclass compare:public binary_function<int,int,bool>{public:    bool operator()(int i,int j) const    {        return i+1==j;    }};int main(){    vector<int> v;    v.push_back(4);v.push_back(0);v.push_back(1);    vector<int>::iterator i=find_if(v.begin(),v.end(),bind2nd(compare(),1));    if(i!=v.end()) cout<<i-v.begin();    return 0;}Trong ví dụ trên, đối số thứ hai của compare() đã được ràng buộc bằng 1, compare() trở thành một predicate chỉ có một đối số làđối số còn lại của compare() ban đầu, và find_if chỉ việc truyền tham số là iterator trỏ đến các phần tử của v vào đối số này, quátrình chạy vòng lặp diễn ra giống như saucompare()(4,1) //phép so sánh 4 + 1 == 1 trả về falsecompare()(0,1) //phép so sánh 0 + 1 == 1 trả về truecompare()(1,1) //phép so sánh 1 + 1 == 1 trả về false (thực ra không có phép so sánh này, hàm đã trả về iterator rồi)Một số hàm thường dùng của thư viện algorithmHàm sort
  • 49. CODEvector<int> v;Hàm này có 2 phiên bảnSắp xếp lại một chuỗi phần tử theo thứ tự tăng dần (ascending)CODEsort (v.begin(),v.end());Sắp xếp lại một chuỗi phần tử thỏa một binary predicateCODEtemplate<typename T>class Bigger{public:    bool operator()(const T& t1,const T& t2){return t1>t2;}};template<typename T>class Output{public:    void operator()(const T& t){cout<<t<<endl;}};int main(int argc, char* argv[]){    vector<int> v;for(int i=0;i<10;i++) v.push_back(i);    sort(v.begin(),v.end(),Bigger<int>());    for_each(v.begin(),v.end(),Output<int>());    return 0;}Hàm transformCODEvector<int> v1;for(int i=0;i<6;i++) v1.push_back(i);Hàm này có hai phiên bản:CODEint increase(int i){return ++i;}vector<int> v2;v2.resize(v1.size());transform(v1.begin(),v1.end(),v2.begin(),increase);Phiên bản thứ nhất sẽ lấy tất cả phần tử từ v1.begin() đến v1.end(), transform chúng bằng hàm increase, sau đó chép giá trị đãtransform vào bắt đầu từ v2.begin()CODEint addition(int i,int j){return i+j;}vector<int> v3;v3.resize(v1.size());transform(v1.begin(),v1.end(),v2.begin(),v3.begin(),addition);Phiên bản thứ hai sẽ lấy tất cả phần tử từ v1.begin() đến v1.end(), transform chúng bằng hàm addition với đối số thứ hai là tất cảphần tử từ v2.begin(), sau đó chép giá trị đã transform vào bắt đầu từ v3.begin()Một số hàm thường dùng của thư viện functionalCác hàm toán học cơ bảnBao gồm cộng (plus) trừ (minus) nhân (multiplies) chia (divides) chia lấy dư (modulus) đổi dấu (negate) Các hàm này rất đơngiản, ví dụ negate
  • 50. CODEint a[]={1,-2,3};transform(a,a+3,a,negate<int>());for_each(a,a+3,Output<int>());Ví dụ plusCODEint a[]={1,2,3,4,5};int b[]={6,7};int c[5];transform(a,a+5,b,c,plus<int>());Ở bài trên có một điều đáng chú ý, bạn tự tìm xemVí dụ modulusCODEint a[]={1,2,3,4,5};int b[]={2,2,2,2,2};int c[5];transform(a,a+5,b,c,modulus<int>());Cái ví dụ hàm modulus này hơi … kì kì. Modulus là một binary function, giả sử bây giờ chúng ta muốn các phần tử của a luônmodulus cho 2 thì làm thế nào ? Phải ràng buộc toán hạng cho modulus để nó trở thành một unary function thôi.CODEint a[]={1,2,3,4,5};int b[5];transform(a,a+5,b,bind2nd(modulus<int>(),2));Các hàm so sánhBao gồm equal_to (==) not_equal_to (!=) greater (>) less (<) greater_equal (>=) less_equal(<=) logical_and (&&) logical_or(||) logical_not (!)Các hàm này cách dùng y như nhau, lấy một ví dụ hàm greaterCODEint a[]={3,2,5,1,4};sort(a,a+5,greater<int>());for_each(a,a+5,Output<int>());Giả sử ta muốn dùng hàm count_if với hàm greater, trả về số phần tử nhỏ hơn 3 chẳng hạn. Ta làm thế nào ? greater là mộtbinary function, lại phải ràng buộc toán hạng cho nó với đối số thứ nhất là 3 rồiCODEint a[]={3,2,5,1,4};cout<<(int)count_if(a,a+5,bind1st(greater<int>(),3));for_each(a,a+5,Output<int>());
  • 51. BÀI 11 – ASSOCIATIVE CONTAINER (CÁC BỘ LƯU TRỮ LIÊN KẾT)Bao gồm map (ánh xạ) multimap (đa ánh xạ) set (tập hợp) multiset (đa tập hợp)Sự khác nhau giữa các associative container và sequential container ở một điểm:-các sequential container lưu trữ các phần tử (gọi là các value) và các value này được truy xuất tuần tự theo vị trí của chúng trongbộ lưu trữ-các associative container lưu trữ các phần tử (gọi là các value) và các khóa (gọi là các key) liên kết với các value và các value nàyđược truy xuất theo các key mà chúng có liên kếtMapCODE#include<map>map<char*,int> ánh xạ từ một char* đến một intmap<char*,int> mapInt;mapInt["one"] = 1;cout<<mapInt["one"];Ánh xạ từ một key đến một value. Ví dụ sau key là lớp string, value là lớp PersonCODEclass Person{public:string name;    Person(string name):name(name){}    friend ostream& operator<<(ostream& os,const Person& p)    {        os<<p.name;return os;    }};typedef map<string,Person> MP;typedef MP::iterator MPI;typedef MP::const_iterator MPCI;typedef MP::value_type MPVT;void display(const MP& mp){    for(MPCI i=mp.begin();i!=mp.end();++i) cout<<(*i).first<<" "<<(*i).second<<endl;}int main(){    MP mapPerson;Person p("Viet");    mapPerson.insert(MPVT("one",p));    display(mapPerson);    return 0;}Giải thích:value_type dùng để khởi tạo một cặp (key,value) cho một ánh xạ. Còn một cách khác là dùng lớp pair của thư viện utility. Có 2cáchCách một là khởi tạo luôn một instance của lớp pairCODE#include<utility>mapPerson.insert(pair<string,Person>("two",Person("Nam")));Cách hai là dùng hàm make_pairCODEpair<string,Person> pr = make_pair(string("two"),Person("Nam"));
  • 52. mapPerson.insert(pr);value_type thực chất cũng là một pairComparatorMột functor dùng để so sánh, sắp xếp, etc các phần tử trong một map gọi là một comparator. Khi đó map thay vì có 2 argumentnhư map<key K,value V> thì có 3 argument là map<key K,value V,comparator C>Dùng comparator để so sánhCODEclass comparePerson{public:    bool operator()(Person p1,Person p2){        return p1.name.compare(p2.name);    }};typedef map<Person,int, comparePerson> MAP;MAP pMap;Person p=new Person(...);MAP::iterator i=pMap.find(d);if(i==pMap.end()) pMap.insert(MAP::value_type(d,1));Dùng comparator để sắp xếpCODEclass comparePerson{public:    bool operator()(const Person& p1,const Person& p2)    {        return p1.name.compare(p2.name);    }};typedef map<string,Person,comparePerson> MP;MP mapPerson;Person p("Viet");mapPerson.insert(pair<string,Person>("one",Person("Nam")));mapPerson.insert(pair<string,Person>("two",Person("Viet")));mapPerson.insert(pair<string,Person>("three",Person("An")));display(mapPerson);Bạn lưu ý là tất cả các asociative container đều có xây dựng sẵn comparator mặc định là less<key> (trong thư viện functional)Nghĩa là khi bạn khai báoCODEmap<char*,int> mapInt;thực ra làCODEmap<char*,int,less<char*> > mapInt;Ví dụCODEtypedef map<char*,int> MI;typedef map<char*,int>::iterator MII;MI m;m["c"] = 1;m["b"] = 2;m["a"] = 3;
  • 53. for(MII i=m.begin();i!=m.end();++i)    cout<<(*i).first<<" "<<(*i).second<<endl;Chạy thử bạn sẽ thấy các value trong map đã được sắp xếp lại vị trí theo các key của chúngcomparator dùng với các sequential containerCODEclass People{    public:int age;    People(int age){(*this).age=age;}};class AgeSort{    public:bool operator()(const People*& a,const People*& b)    {        return (*a).age>(*b).age;    }};typedef list<const People*> LP;int main(){    const People* p1 = new People(5);const People* p2 = new People(7);    LP p;p.push_back(p1);p.push_back(p2);    p.sort(AgeSort());//using sort with comparator    for(LP::const_iterator i=p.begin();i!=p.end();++i) cout<<(**i).age;    return 0;}multimapVới map thì mỗi key chỉ ánh xạ đến một và chỉ một value. Với multimap thì mỗi key có thể ánh xạ đến nhiều hơn một value, nóicách khác là nhiều value trong multimap có chung một keyCODE#include<map>typedef multimap<string,Person> MP;MP multimapPerson;multimapPerson.insert(MPVT("one",Person("Nam")));multimapPerson.insert(MPVT("one",Person("Viet")));display(multimapPerson);typedef multimap<Person,int,comparePerson> MAP;Cũng chính vì lí do nhiều value trong multimap có thể có chung một key nên multi không có operator[] như map, tức là bạn khôngthể gọi multimapPerson[“one”]setset cũng giống map ngoại trừ một điều, key cũng chính là valueCODE#include<set>set<int> s;for(int j=0;j<6;j++) s.insert(rand());for(set<int>::iterator i=s.begin();i!=s.end();++i) cout<<(*i)<<endl;set dùng với comparator (greater đóng vai trò comparator)CODEset<int,greater<int> > s;for(int j=0;j<6;j++) s.insert(rand());for(set<int,greater<int> >::iterator i=s.begin();i!=s.end();++i)
  • 54.     cout<<(*i)<<endl;set không có operator[]multisetmultiset cũng giống set ngoại trừ một điều, mỗi key có thể ánh xạ đến nhiều hơn một value, nói cách khác là nhiều value trongmultiset có chung một keyBạn có thể thắc mắc điều này chẳng có ý nghĩa gì, vì trong set thì key cũng chính là value, Không, chúng có khác đấy, thử xemnhé:CODE#include<set>set<int> s;s.insert(1);s.insert(1);for(set<int>::iterator i=s.begin();i!=s.end();++i) cout<<(*i)<<endl;multiset<int> ms;ms.insert(3);ms.insert(3);for(multiset<int>::iterator mi=ms.begin();mi!=ms.end();++mi)    cout<<(*mi)<<endl;
  • 55. BÀI 12: CÁC BỘ TƯƠNG THÍCH VÀ CÁC THƯ VIỆN KHÁCcontainer adapter (các bộ tương thích lưu trữ)Bao gồm stack, queue và priority_queueCác bộ tương thích lưu trữ, dưới đây gọi là các bộ tương thích, làm các bộ lưu trữ khác trở nên tương thích với nó bằng cách đónggói (encapsulate) các bộ lưu trữ khác trở thành bộ lưu trữ cơ sở của nó. Ví dụCODEstack<int,vector<int> > s;Khi đó vector trở thành bộ lưu trữ cơ sở của bộ tương thích stackNếu không khai báo bộ lưu trữ cơ sở, stack và queue mặc định sử dụng deque làm bộ lưu trữ cơ sở, trong khi priority_queue mặcđịnh sử dụng vector làm bộ lưu trữ cơ sở, có nghĩa là khi khai báoCODEstack<int> s;thực ra làCODEstack<int,deque<int> > s;stack và queuestack là LIFO, queue là FIFO, xem thử sự khác biệt qua ví dụ palindrome sau(lưu ý, palindrome tức là một từ đọc xuôi hay ngược đều như nhau, ví dụ 12321, level, aka)CODE#include <stack>#include <queue>using namespace std;int main(){    stack<char> stackInt;queue<char> queueInt;    char a;//store temp user input    int n;//no of numbers user intend to input    cout<<"how many elements:";cin>>n;    for(int i=0;i<n;i++){        cin>>a;        stackInt.push(a);        queueInt.push(a);    }    for(int i=0;i<n;i++){        if(stackInt.top()!=queueInt.front()){            cout<<"not a palindrome"<<endl;break;        }        stackInt.pop();queueInt.pop();        if(i==n-1) cout<<"a palindrome"<<endl;    }}Lưu ý 2 cả stack và queue đều có các hàm sauvoid push(T) thêm phần tử vàovoid pop(T) gỡ phần tử rastack có thêm hàmT top() truy xuất phần tử tiếp theoqueue có thêm hàmT front() truy xuất phần tử tiếp theoT back() truy xuất phần tử cuối cùng của queue
  • 56. priority_queuepriority_queue là queue trong đó phần tử đầu tiên luôn luôn là phần tử lớn nhất theo một tiêu chuẩn sắp xếp nào đópriority_queue giống như khái niệm heap (đống) mà ta đã biết (heap và giải thuật heapsort trong môn CTDL)Thực ra priority_queue chỉ là queue mặc định có cài sẵn thêm comparator less<T> giống như các associative container thôi. Ta cóthể cài lại comparator do ta định nghĩa cho nó (ví dụ bài dưới đây cài greater<T>)CODE#include <queue>class Plane{    int fuel;public:    Plane(int fuel){(*this).fuel=fuel;}    friend ostream& operator<<(ostream& os,const Plane& p){        os<<p.fuel<<endl;return os;}    bool operator>(const Plane& p) const{        return fuel>p.fuel;}};typedef priority_queue<Plane,vector<Plane>,greater<Plane> > PriorityQueuePlane;int main(){    vector<Plane> vP;    vP.push_back(Plane(4));vP.push_back(Plane(7));    vP.push_back(Plane(3));vP.push_back(Plane(9));    PriorityQueuePlane v(vP.begin(),vP.end());    while(!v.empty()){        cout<<v.top();v.pop();    }     return 0;}Lưu ý là priority_queue có push, pop và top, không có front và backiterator adapter (các bộ tương thích con trỏ)Các bộ tương thích iterator làm các container và iterator khác trở nên tương thích với nó.bằng cách đóng gói (encapsulate) cáccontainer và iterator khác trở thành container và iterator cơ sở của nó. Chúng có dạng khai báo cơ bản như sauCODE#include<iterator>template<class Container,class Iterator>class IteratorAdapter{    //nội dung};IteratorAdapter<vector<int>,vector<int>::iterator> vectorIntAdapter;Không học thêm về iterator và iterator adapterfunction adapter (các bộ tương thích hàm)Có 2 bộ tương thích hàm chúng ta đã học trước đó là bind1st và bind2nd. Chúng ta sắp học not1, not2, mem_fun, mem_fun_refvà ptr_fun. Tất cả đều nằm trong thư viện functionalnot1Đổi giá trị trả về của một unary predicate từ false thành true và ngược lại, unary predicate phải được định nghĩa là unary_functionVí dụ dùng IsOdd tìm các số chẵn (nghĩa là IsOdd trả về not(true))CODEclass IsOdd:public unary_function<int,bool>{public:bool operator()(const int& n) const{return n%2;}};int main(int argc, char* argv[]){    int a[] = {1,2,3,4,5};
  • 57.     cout<<count_if(a,a+5,not1(IsOdd()))<<endl;    return 0;}not2Đổi giá trị trả về của một binary predicate từ false thành true và ngược lại, binary predicate phải được định nghĩa làbinary_functionVí dụ dùng compare để so sánh 2 mảng với các phần tử không bằng nhau (nghĩa là compare trả về not(true))CODEclass compare:public binary_function<int,int,bool>{public:bool operator()(int i,int j) const{return i==j;}};int main(int argc, char* argv[]){    int a[] = {1,2,3,4,5};    int b[] = {6,7,8,9,10};    cout<<equal(a,a+5,b,not2(compare()))<<endl;    return 0;}ptr_funChuyển một con trỏ hàm (function pointer) thành một functorCODEint addition(int a,int b){return a+b;}int output(int a){cout<<a<<endl;return 0;}int(*cong)(int,int) = addition;int(*xuat)(int) = output;int main(){    int a[] = {1,2,3,4,5};    int b[] = {6,7,8,9,10};    int c[5];    transform(a,a+5,b,c,ptr_fun(cong));    for_each(c,c+5,ptr_fun(xuat));    return 0;}Ở đây chúng ta có binary function là addition và unary function là output, và binary function pointer là cong và unary functionpointer là xuat, và ta dùng ptr_fun để chuyển các con trỏ hàm này thành binary functor và unary functor để đóng vai trò predicatedùng trong hai hàm transform và for_eachptr_fun chỉ dùng cho stand-alone và static member function, với non-static member function phải dùng mem_fun haymem_fun_refmem_funChuyển một hàm thành viên (member function) của một lအ