Thứ Bảy, 25 tháng 6, 2016

create-facebook-live-streaming-video
Hướng dẫn tạo Live Stream Video trên Facebook trực tiếp bằng máy tính. Creating a stream live video on Facebook with just a few simple steps. Facebook Live Streaming Video by Juno_okyo.
Read More

Thứ Sáu, 24 tháng 6, 2016

facebook-bug-delete-any-video-exploit.png
Vào ngày 10/06/2016 vừa qua, Pranav Hivarekar - một nhà nghiên cứu bảo mật đã phát hiện ra một lỗ hổng trong tính năng mới của Facebook là bình luận bằng video.

Câu chuyện phía sau

Tôi vô tình lướt qua ghi chú "New: Videos in Comments!" viết bởi Bob Baldwin - một kỹ sư làm việc tại Facebook. Ghi chú này viết về việc Facebook tung ra tính năng mới bình luận với video. VD: giờ đây, người dùng được phép tải lên một video trong bình luận.
Read More

1. Nghi ngờ về lỗ hổng XSS trên phimmoi.net

phimmoi.net-xss-vulnerability
Đang ngồi code thì ông anh gửi cho cái link phim kêu vào coi, ban đầu cũng không muốn coi đâu vì thấy IMDB thấp quá. Mà sau ông ý gửi thêm cái link Youtube này nữa nên mình thay đổi ý định liền.
Read More

Thứ Tư, 8 tháng 6, 2016

CONST
const int p int const p là như nhau
int* 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ày trỏ đến một nơi khác được nữa
CODE
int 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ỏ đến
CODE
int 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ên
int 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ên
Mộ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áo const nếu nó có ít nhất một tham số const
CODE
class 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ố getAge
Hàm số trả về const
Ví dụ chúng ta viết một hàm như sau
CODE
int& 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ểm
CODE
hello() = 3;
Nên chúng ta phải thay đổi để nó trả về const
CODE
const int& hello()
{
int temp=5;
return temp;

Bảo vệ tham số
Hẳn bạn còn nhớ ví dụ này chứ
CODE
void 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ảng
CODE
void 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ố
CODE
void dosomething(const char* a){...}
char* m = "hello";
dosomething(m);

--------------------------------------------------------------------------------
STATIC
Mộ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
Mộ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 đó
CODE
class MyClass
{
public:
static int i;
};
int MyClass::i; //tồn tại độc lập, không phụ thuộc instance
int 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
CODE
class 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ào
CODE
void in(int a); //chỉ cần khai báo bình thường
Hàm dưới đây không thể ném ra bất cứ ngoại lệ nào
CODE
void 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ớp
nào hết
Chúng ta bắt ngoại lệ bằng try catch như Java
CODE
class 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;
}
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;
CODE
class 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! That's 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ớp
CODE
int 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;
}
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 nhau
CODE
int 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ình
Chú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(...)
CODE
int 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 C
int 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ỗi
dynamic_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 downcast
bằng static_cast
CODE
class Base{};
class Derived : public Base{};
Base* b=new Derived;
Derived* d = static_cast<Derived*>(b);
bằng dynamic_cast
CODE
class 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ó 2 cách thực hiện upcast
bằng static_cast
CODE
class Base{};
class Derived : public Base{};
Derived* d = new Derived;
Base* b = static_cast<Base*>(d);
bằng dynamic_cast
CODE
class 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)
CODE
int 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)
CODE
const 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;
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ây giờ ta cũng chưa phải dùng, sẽ học sau.
Trình biên dịch và template
Trong 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ủa template.
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ủa template, 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ác
hà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ớp template 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ải include 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 typename
Về 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ụ
CODE
template<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ểu T với ptr Lúc này chúng ta bắt buộc phải dùng từ khóa typename
CODE
template<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 template
template<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ớp template đó
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ề 0
CODE
template<class T>
class pair {
T value1, value2;
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 int
template<>
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óa
template<>
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ư sau
int n=(int)45.87;
Trong C++ có 1 cách ép kiểu dữ liệu như sau
int 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++ sau
Diễn dịch đối số (argument deduction)
Xem lại hàm template dưới đây
template <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 int
max(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ái kiểu int, một cái kiểu double
Có 2 cách xử lí chuyện này
Cách 1: casting (ép kiểu) tham số đầu tiên
max(static_cast<double>(7), 5.2); //lúc này T là kiểu double, 2 đối số đều cùng kiểu double
Cách 2: explicit specialization (chuyên môn hóa cụ thể) cho T thành double
max<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ểu
dữ liệu đã biết
Đối số kiểu primitive, ví dụ kiểu int
CODE
template<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ác
CODE
#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ều hà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.
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, truy xuấ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 đó.
CODE
template<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);}
};
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.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
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ó bao
nhiê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ảng
cá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 function
Chuẩ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"
#endif
Chuẩn bị một tập tin tên là “array.cpp”
CODE
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>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;
}
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ên
khi 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ủa
hà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 khi
làm các game lớn.
Biên dịch lại mã này với GCC
Khô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ập
tin 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ưng
khô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 *nix
hết.

------------------------------------------------------
Viết operator overload và copy constructor
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ẽ overload
toá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 function
CODE
#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>
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;
}
Hà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ậ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 0
}

-------------------------------------------------------------
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.getmaximum();
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
CODE
int 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ên
hơ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 pointer
CODE
int* p; //một con trỏ p bình thường
int*& r = p; //tham chiếu r là nickname mới của p
r = new int; //tương đương với p = new int
*r = 5; //tương đưong với *p = 5
cout<<*p; //tương đương với cout<<*r
Và như vậy, vấn đề khó khăn với dữ liệu kiểu mảng đã được giải quyết.
CODE
template<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.
Firefox-add-on-development-tips
Hôm nay vừa port xong Facebook Original Emoticons sang phiên bản Firefox, tôi quyết định gửi add-on của mình lên AMO (là trang liệt kê các add-on Firefox cho người dùng cài đặt, giống như Chrome Store vậy). Ban đầu cũng chỉ định tải tập tin XPI lên cho các bạn tải về tự cài thủ công thôi, vì trước đây cũng gửi một add-on mà không được duyệt. Thôi thì quyết định thử lại vận may một lần nữa vậy.
Read More
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.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ạ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.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ử ==
CODE
class 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ế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.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;
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)
CODE
class 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 constructor
CODE
class 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;
}

------------------------------------------------------------------------
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.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 đấ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.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à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.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 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{
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ó
CODE
MusicPlayer *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ượng
CODE
class MusicPlayer... là một lớp trừu tượng
class 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)
CODE
char destArray[10];char srcArray[]="panther";
strcpy(destArray, srcArray);
strcpy(destArray, srcArray,strlen(srcArray));
strcat(s1,s2); //thêm (append) s2 vào s2
strncat(s1,s2,n); //thêm (append) n kí tự đầu tiên của s2 vào s1
strlen(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>b
atoi, atof, atoll convert một char array thành integer, float hay long, 3 hàm này trong stdlib.h
char *s = "123.45";
int i=atoi(s);
float f=atof(s);

--------------------------------------------------------------
Nhắc lại một chút về chuỗi (string)
CODE
using 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ồi
if(s1.compare(s2))
*cộng chuỗi
string s1,s2;s1+=s2;s1+='o';
s1.append(s2); //y nhu s1+=s2
s1.append(s2,3,string::npos); //thêm vào s1 từ kí tự thứ 3 đến hết s2
s1.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 đa
if 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";
}

Categories

Sample Text

Được tạo bởi Blogger.

Must Read

Biểu mẫu liên hệ

Tên

Email *

Thông báo *

Popular Posts

Video

Popular Posts

Our Facebook Page