LẬP TRÌNH C/C++ NÂNG CAO – LẬP TRÌNH C/C++ NÂNG CAO Yêu cầu trước khi đọc: học xong Lập trình C/C++ – StuDocu

LẬP TRÌNH C/C++ NÂNG CAO

Yêu cầu trước khi đọc: học xong Lập trình C/C++ căn bản
BÀI 1: NHẮC LẠI VỀ C/C++
Nhập xuất cơ bản

CODE
#define max(a,b) (a>b)?a:b    //khai báo macro
typedef unsigned int byte;    //định nghĩa kiểu dữ liệu
const float PI=3;         //khai báo hằng số
char c;char s[20];

Cách của C

CODE
//không dùng scan nếu muốn nhập khoảng trắng
gets(s);            //có thể nhập khoảng trắng
puts(s);
fflush(stdin);        //xóa bộ đệm nhập
c=getchar();
putchar©;

Cách của C++

CODE
//không dùng cin>> nếu muốn nhập khoảng trắng
cin(a,21);    //có thể nhập khoảng trắng
cout<<a;
cin();        //xóa bộ đệm nhập

Con trỏ cơ bản

CODE
int 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 dinh
p=&a;            //hop le, gan dia chi ma p tro den
*p=3;            //hop le, gan gia tri tai dia chi ma p tro den
cout<<p<<endl;    //cai gi do bat ki, dia chi cua a
cout<<&p<<endl;    //cai gi do bat ki, dia chi cua p
cout<<*p<<endl;    //3,dau * luc nay mang y nghia "gia tri tai dia chi cua"

Truyền giá trị cho hàm
Trong C có khái niệm con trỏ (pointer) Trong C++ có thêm khái niệm tham chiếu (reference)

CODE
int a;
int& b=a;

Lúc này biến a có một cái nickname là b
Như vậy có tất cả 3 cách viết hàm và truyền tham số
Cách 1:

CODE
void add10(int a)
{
    a=a+10;
}

gọi:
add10(n);

Không hiệu quả, a vẫn giữ nguyên giá trị
Cách 2:

CODE
void add10(int *a)
{
    *a=*a+10;
}
gọi:
add10(&n);

Hiệu quả.
Cách 3:

CODE
void 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ên

CODE
int a[3];

Truyền dữ liệu trực tiếp theo kiểu C, cách 1

CODE
for(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 2

CODE
for(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 1

CODE
for(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 2

CODE
for(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ên
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 1

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ực
Cách dùng biến tạm

CODE
float 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ỏ

CODE
float 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ểu

CODE
float 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ểu

CODE
float a[3][2];float p;p=(float)a;
for(int i=0;i<3;i++)
    for(int j=0;j<2;j++)
        printf("%f\n",(p+i2+j));

Nhập mảng số thực 2 chiều bằng cách dùng malloc

CODE
float** 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));

Xuất mảng số thực 2 chiều bằng cách dùng malloc

CODE
float** p;p=(float**)malloc(2);
for(int i=0;i<3;i++)
    for(int j=0;j<2;j++)
        printf("%f\n",(p+i2+j));

Bài này chỉ có giá trị tham khảo, tổng hợp kiến thức.

void add(Student &s)
{
    cin>>s;
    cin();
    cin>>s;
}
add10(a);

Cách 3

CODE
void add(Student *s)
{
    cin>>(*s).name;
    cin();
    cin>>(*s).id;
}
add(&a);

Cách 4

CODE
void add(Student *s)
{
    cin>>s->name;
    cin();
    cin>>s->id;
}
add(&a);

Toán tử sizeof với struct

CODE
struct 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ác

CODE
char a=’z’;    //a=’z’ và giả sử địa chỉ của a=
char *p=&a;    //p=8277 và giả sử địa chỉ của p=
char **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ào

CODE
void increase(void* data,int dataType)
{
    switch(dataType)
    {
    case sizeof(char):

        (((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àm

CODE
int 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ác
hà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.

CODE
inline void display(char *s)
{
    cout<<s<<endl;
}
int main()
{
    display("Hello");return 0;
}

Nhập xuất với tập tin

CODE
#include <fstream>
#include <iomanip>

BÀI 3: NHẮC LẠI VỀ LỚP

Cơ bản về lớp

CODE
class Date{
    int day;
public:
    Date(int,int a=1);
    int month;
    void setDay(int);
    void output();
};
int main(){
    Date d(6);
    d=3;
    d(25);
    d();
    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ạo
Chúng ta có thể viết một hàm khởi tạo như thế này

CODE
class Student
{
    string name;int age;
public:
    Student(string name,int n):name(name),age(n)
    {
    }
};

Nó tươngđương với

CODE
class Student
{
    string name;int age;
public:
    Student(string name,int n)
    {
        (*this).name = name;
        this->age = n;
    }
};

Hàm bạn (friend function)

CODE
class Student{
public:
    int id;
    friend bool equal(const Student&,const Student&);
};
int main(){
    Student s1;s1=2;
    Student s2;s2=3;
    cout<<equal(s1,s2);
}
bool equal(const Student& s1,const Student& s2){
    return (s1.id==s2);
}

Overload toán tử (operator overload)
Ví dụ dưới sẽ overload toán tử ==

CODE
class Student{
public:
    int id;
    friend bool operator==(const Student&,const Student&);
};
int main(){
    Student s1;s1=2;
    Student s2;s2=3;
    cout<<((s1==s2)?"equal":"unequal");
}
bool operator==(const Student& s1,const Student& s2){
    return (s1.id==s2);
}

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>>(a) hoặc operator>>(cin,a) Overload 2 toán tử nhập và xuất này hết
sứ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ề sau
game cái ưu tiên hàng đầu là bộ nhớ, đừng đểlại rác.

CODE
class 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;
    ins>>d;
    ins();    //phải xóa bộ đệm
    return ins;
}
ostream& operator<<(ostream& outs,const Date& d){
    outs<<d<<"/"<<d;

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ông
Chúng ta vẫn thường cấp phát bộ nhớ như sau

CODE
char *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ày
Cách một là dùng từ khóa nothrow. Vì thế CPP vẫn tạo ra một pointer nhưng là 0

CODE
p=new (nothrow) char[i+1];
if(p==0) cout<<"Can’t allocate memory";

Cách hai là bắt cái ngoại lệ ấy, Ðó là ngoại lệ std::bad_alloc

CODE
try{
    p=new char[i+1];
}catch(std::bad_alloc &mae){
    cerr<<"failed to allocate memory"<<mae();
    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đấy

CODE
char 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 calloc
p=(char*)calloc(i,sizeof(char));

Toán tử gán (assignment operator)

CODE
class 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;

        c = new char[strlen(src)+1];
        strcpy(this->c,src);
    }
    return *this;
}
bool operator!=(const Base& b1,const Base& b2){
    return(strcmp(b1,b2));
}
Và chúng ta có thể gọi toán tửnày
Base s2=s1;

Thừa kế (inheritance)
Trong C có thể sinh ra bug, trong C++ chúng sẽ được thừa kế.

CODE
class 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)

CODE
class 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();
    DVD d(2);d();
}

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 MusicPlayer
trở 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ữa

CODE
class MusicPlayer{

string s1,s2;s1+=s2;s1+=’o’;
s1(s2);        //y nhu s1+=s
s1(s2,3,string::npos);        //thêm vào s1 từ kí tự thứ 3  đến hết s
s1(7,s2);        //thêm s2 vào sau kí tự thứ 7 của s
*kích cỡ (capacity)
s() trả về kích cỡ tối đa
if s()=15, s()=16 (16-byte)
if s()=17, s()=32 (two 16-byte)
*truy xuất chuỗi
#include <stdexcept>
try{
    cout<<s(100);
}catch(out_of_range& e){
    cout<<"invalid index";
}

BÀI 4: TEMPLATE
Hàm template

Giả 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ố
CODE
int 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ậy
CODE
double 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)
CODE
Person 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à template
CODE
template<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 
}

template với nhiều hơn một kiểu dữ liệu
CODE
template<class T,typename U>void func(T a,U b);

Dùng template với mảng
CODE
template<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)
CODE
template<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 main
CODE
pair<int> myobject(155,36);
myobject();

Thật tuyệt, đúng không?
Vấn đề khôngđơn giản như vậy.
Đau đầu
Xem lại hàm template dưới đây
CODE
template<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à b
CODE
char* 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à b

div , id: post-25916, class: postcolor

BÀI 5: TEMPLATE (TIẾP)

Lại đau đầu
Ta 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ư sau

CODE
template<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ết

CODE
char *list[]={"zero","one","two"};    //thực ra là mảng 2 chiều thôi
search(list,3,"two");    //ồkhông, lại so sánh memory address nữa rồi

Như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ữa
Giải quyết
Chươ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 to
pointer để 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);

}

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()!=p2();
    }
    friend ostream& operator<<(ostream& os,const Person& p)
    {
        os<<p()<<endl;
        return os;
    }
};
int main()
{
    Array<Person> a(3);
    a(Person(5),2);
    cout<<a[2];
    Person b;
    a(b,4);
    for(int i=0;i<4;i++) *(b+i)=Person(i+2);
    cout<<a(Person(5))<<endl;
    cout<<a(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