Lập trình C/C ++Nâng cao Công nghệ Website | Đại học Bách Khoa, Đại học Đà Nẵng

Lập trình C/C ++Nâng cao Công nghệ Website | Đại học Bách Khoa, Đại học Đà Nẵng giúp sinh viên tham khảo, ôn luyện và phục vụ nhu cầu học tập của mình cụ thể là có định hướng, ôn tập, nắm vững kiến thức môn học và làm bài tốt trong những bài kiểm tra, bài tiểu luận, bài tập kết thúc học phần, từ đó học tập tốt và có kết quả cao cũng như có thể vận dụng tốt những kiến thức mình đã học

LP 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 np xut cơ b
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.14;//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.getline(a,21);//cóth nh pkho ngtr ng
cout<<a;
cin.get();//xóab mnh pộ đệ
Con tr ncơ b
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"
Truyn 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ó tt 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;
}
gi:
add10(&n);
Hi .u qu
Cách 3:
CODE
voidadd10(int&a)
{
a=a+10;
}
gi:
add10(n);
Hiu qu, tin hơn cách 2.
Nh lip xut d u v i ki u m ng s nguyên
CODE
inta[3];
Truy lin d u trc 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 lin d u trc 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 lin d u trc 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 lin d u trc 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 lip xut d u bng hàm vi kiu mng s nguyên
Nh p xu t dliu b ng hàm v i kiu m ng s nguyên theo ki u C, cách 1
CODE
void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 lip xut d u bng hàm vi ki u m ng s nguyên theo ki u C, cách 2
CODE
void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 lip xut d u bng hàm vi ki u m ng s nguyên theo ki u C++, cách 1
CODE
void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 lip xut d u bng hàm vi ki u m ng s nguyên theo ki u C++, cách 2
CODE
void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 li th cp xut d u v i ki u m ng s
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 thp mng s c 2 chiu bng 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 tht mng s c 2 chiu bng 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+i*2+j));
Nh thp mng s c 2 chiu bng 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 tht mng s 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+i*2+j));
Bài này ch có giá tr tham kh o, t ng h p ki n th c. ế
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)
CODE
structStudent
{
intid;
};
Student*s;
Studentm;
s=&m;
s->id=3;//means(*s).id
cout<<m.id;
Sao chép c u trúc
CODE
struct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 m
Student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úc
CODE
struct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 u vào c u trúc.để ữ liệ
Cách 1
CODE
voidadd(charname[],int*place)
{
cin>>name;
cin.get();
cin>>*place;
}
add(a.name,&a.id);
Cách 2
CODE
voidadd(Student&s)
{
cin>>s.name;
cin.get();
cin>>s.id;
}
add10(a);
Cách 3
CODE
voidadd(Student*s)
{
cin>>(*s).name;
cin.get();
cin>>(*s).id;
}
add(&a);
Cách 4
CODE
voidadd(Student*s)
{
cin>>s->name;
cin.get();
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=8277 ử đị
char*p=&a;//p=8277vàgi s ach c ap=6194 ử đị
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 n b t c t ki u d u nào để trỏ đế ứ mộ ữ liệ
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 n m t hàm để trỏ đế
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 c thi c c nhanh, nên s ng v i các hàm th ng xuyên ph i s độ thự ử dụ ườ ử 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>
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 nh tở một file dùng cho cả ập và xuấ
CODE
fstreamf;
f.open("st.txt",ios::in|ios::out);
m ts ch haydùng ế độ
ios::inngh alành pvàoĩ
ios:outngh alàxu trat ptint ut ptinĩ ừ đầ
ios::appngh alàthêmd li uvàot ptin(appending)ĩ
Tập tin header
Tạo m t t ập tin header có tên là myfile.h
#ifndef MYFILE_H
#define MYFILE_H
……
#endif
trong tập tin cpp thêm vào dòng
#include "myfile.h"
BÀI 3: NH C L I V P Ề LỚ
Cơ b l pản về
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 t m t hàm kh i t o nh nàyể viế ư thế
CODE
classStudent
{
stringname;intage;
public:
Student(stringname,intn):name(name),age(n)
{
}
};
Nó t ng v iương đươ
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 i s overload toán t ==ụ dướ
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 p và xu t (input >> và output <<)ử nhậ
Mọi ng i u bi t cin>>a là g i toán t p cin.operator>>(a) ho c operator>>(cin,a) Overload 2 toán t p và xu t này hườ đề ế ử nhậ ử nhậ ết
sức quan tr ng v sau. Nhân ti n m ỗi khi c p phát b , dùng xong ph i luôn h y i thu h i l ộ nhớ đ để i b ã cộ nhớ đ p phát. Vì v sau
game cái u tiên hàng u là b , ng i rác.ư đầ ộ nhớ đừ để lạ
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 p phát bề cấ ộ nhớ
Ði x c nhều gì sẽ ảy ra khi chúng ta không thể ấp phát bộ ? Ví d chúng ta viết 1 game RTS mà m i phe tham chi n có 10 t ế
quân ?
Gi c nhải quyết khi không thể ấp phát bộ ớ thành công
Chúng ta v n th ng c p phát b sau ườ ộ nhớ như
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 p phát b ? CPP s ném (throw) ra m t ngoể cấ ộ nhớ ại lệ. Có 2 cách lí chuyđể xử ệ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 trong Cộ nhớ
Ðừ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 n các subclass c a nó ỏ để trỏ đế
CODE
MusicPlayer*m=newDVD(5);m->play();
Chúng ta cung có th o m ng các con tr a m t l p tr u t ngể tạ ỏ củ ượ
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 mắc lại m t chút v ả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 chuắc lại m t chút v ỗ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";
}
BÀI 4: TEMPLATE
Hàm template
Gi s s chúng ta cần viết một hàm tr về 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 c chúng ta c ng làm nh yđế ố thự ũ ư vậ
CODE
doublemaximum(doublea,doubleb)
{
return(a>b)?a:b;
}
Rồi gi i l p Person chúng ta c ng ph i làm nh y (toán t > ã c overload)ả sử như vớ ũ ư vậ đ đượ
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 n gi n nhđề không đơ ư 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 i ây th c ra là ang so sánh a ch (memory address) c a 2 bi n a và bụ dướ đ đ đị ỉ bộ nhớ ế
CODE
char*a="hello";char*b="world";
cout<<maximum(a,b);
Ví d i ây c ng là ang so sánh a ch (memory address) c a 2 bi n a và bụ dướ đ ũ đ đị ỉ bộ nhớ ế
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 ng 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 đề tưở ư nhỏ nhặ ế đ đầ đ đặ đụ đ đầ ườ
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v icout<<*rương đươ
Và nh y, v n khó kh n v i d u ki u m ng ã c gi i quy t.ư vậ đề ă ữ liệ đ đượ ế
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 t tham chi u mà tham chi u n m t tham chi u khác" hay ế ế đế ỏ đế ư "mộ ế ế đế ế
"một con tr mà tr n m t tham chi u" ỏ đế ế đâu nhá.
Hết khó kh n ch a ? Ch âu.ă ư ưa đ
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 phưng lần này vấn đề ức t p h n nhi u. Ví d ơ ụ nếu là m ng các Person là ng thêm v n p phát b đụ đề cấ ộ 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 u, toán t []. ă ữ liệ
Đặ đầ ư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 p phát b óđể cấ ộ 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 t toán t output << cho m t m ng template class hay so sánh gi ử viế 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 ng cái này: prototype template function (khai báo nguyên m u cho hàm template) ế đượ đ ế ử dụ
(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 t tị mộ ậ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 t tị mộ ậ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 hoải thích: equal và operator<< đều là hai hàm bạn, do đó để ạ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 t này hi u và ng d ng c c kì r c r i nh ng kh i l ượ ĩ thuậ ư ổ nỗ đặ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 o makefile. Trong tr ng b n tôi ch ớ tạ ườ
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 m nh ng ị cấ ư
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 ng l i cách này, mà không ph i dùng n prototype template function ế ũ ử dụ đế
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>
| 1/76

Preview text:

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.14;//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.getline(a,21);//cóthể nhậpkhoảngtrắng
cout<cin.get();//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<

cout<<&p<cout<<*p<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<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 CODE void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 2 CODE void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 1 CODE void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 2 CODE void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<}
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+i*2+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+i*2+j));
Bài này chỉ có giá trị tham khảo, tổng hợp kiến thức.
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) CODE struct Student { int id; }; Student *s; Student m; s=&m; s->id=3; //means (*s).id cout<Sao chép cấu trúc CODE struct 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ạm Student 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úc CODE struct 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 1 CODE
void add(char name[],int *place) { cin>>name; cin.get(); cin>>*place; } add(a.name,&a.id); Cách 2 CODE void add(Student &s) { cin>>s.name; cin.get(); cin>>s.id; } add10(a); Cách 3 CODE void add(Student *s) { cin>>(*s).name; cin.get(); cin>>(*s).id; } add(&a); Cách 4 CODE void add(Student *s) { cin>>s->name; cin.get(); 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=8277 char *p=&a;
//p=8277 và giả sử địa chỉ c a ủ p=6194 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<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<} int main() { display("Hello");return 0; } Nhập xuất với tập tin CODE #include #include int number; ifstream inf;ofstream outf; inf.open("input.txt"); outf.open("output.txt"); while(in>>number)
outf<<"Next is"<inf.close(); outf.close();
Mở một file dùng cho cả nhập và xuất CODE fstream f;
f.open("st.txt",ios :: in | ios :: out); m t ộ số chế độ hay dùng
ios :: in nghĩa là nhập vào
ios:out nghĩa là xuất ra tập tin từ đầu tập tin
ios::app nghĩa là thêm dữ liệu vào tập tin (appending) Tập tin header
Tạo một tập tin header có tên là myfile.h #ifndef MYFILE_H #define MYFILE_H …… #endif
trong tập tin cpp thêm vào dòng #include "myfile.h"
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.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<} 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<}
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<return outs; } int main(){ Date d;
cin>>d;cout<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<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"<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 ?"<} }; class DVD:public MusicPlayer{ public: void Play(){ cout<<"Play on DVD"<} }; 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"<} }; 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 ab 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 try{
cout<}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;
}div, id: post-25916, class: postcolor
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 templateT maximum(T a,T b) { return (a>b)?a:b; } int main() { int a=7;int b=5; cout<return 0 }
template với nhiều hơn một kiểu dữ liệu CODE templatevoid func(T a,U b); Dùng template với mảng CODE
templatevoid print(T (&a)[size]) { for(int i=0;i}
Lớp template (template class) CODE templateclass pair { T values[2]; public: pair(T first,T second) {
values[0]=first; values[1]=second; } T getmaximum(); }; templateT pair::getmaximum() {
return (values[0]> values[1])? values[0]: values[1]; } Trong hàm main CODE pair 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 templateT 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<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 CODE int a[3],b[3];
cout<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
templateT* maximum(T*& a,T*& b) { return (*a>*b)?a:b; } int main() { char* a="bb"; char* b="aa"; cout<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. 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
templateint search(T a[],int n,T key) { int index=0;
while(indexif(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 using namespace std; templateclass 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 }; templateArray::Array(int n) { size=n; array = new T[size]; } templateArray::~Array() { delete [] array; }
templatevoid Array::setValue(const T& value,int n) { *(array+n) = value; }
templateT& Array::getValue(int n) { return *(array+n); }
templatevoid Array::makeArray(T *&arr,int n) { arr = new T[n]; }
templateT& Array::operator[](int i) { return *(array+i); }
templateint Array::seek(const T& key) { int index=0;
while((indexif(index==size) return -1; else return index; }
templateint Array::search(const T* list,int size,const T key) { int index=0;
while((indexif(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<return os; } }; int main() { Array a(3); a.setValue(Person(5),2); cout<Person* b; a.makeArray(b,4);
for(int i=0;i<4;i++) *(b+i)=Person(i+2); cout<cout<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 using namespace std; templateclass Array;
templatebool equal(const Array&,const Array&);
templateostream& operator<<(ostream&,const Array&); templateclass Array { T* array;int size; public: Array(int n); ~Array();
void setValue(const T&,int n);
friend bool equal <>(const Array&,const Array&);
friend ostream& operator<< <>(ostream&,const Array&); }; #include "array.cpp" #endif
Chuẩn bị một tập tin tên là “array.cpp” CODE templateArray::Array(int n) { size=n; array = new T[size]; } templateArray::~Array() { delete [] array; }
templatevoid Array::setValue(const T& value,int n) { *(array+n) = value; }
templatebool equal(const Array& a1,const Array& a2) { return a1.size==a2.size; }
templateostream& operator<<(ostream& os,const Array& a) { for(int i=0;ireturn 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<return os; } }; int main() { Array a(3); a.setValue(Person(24),0); a.setValue(Person(15),1); a.setValue(Person(5),2); cout<Array b(3); cout<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 V
ộ isual 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 #include using namespace std; template class Array { public: int size; T* elems; Array(int); Array(const Array*&);
void setValue(const T&,int i); T& getValue(int n);
Array& operator=(const Array*&);
friend bool operator!=(const Array&,const Array&); }; template Array::Array(int size) { elems = new T[size]; } template
void Array::setValue(const T& value,int i) { *(elems+i) = value; } template T& Array::getValue(int i) { return *(elems+i); } template
Array::Array(const Array*& src) { size=src.size; elems = new T[size];
for(int i=0;i*(elems+i) = *(src.elems+i); } template
Array& Array::operator=(const Array*& src) { if(*this!=src) //to avoid self-assignment { size=src.size; elems = new T[size];
for(int i=0;i*(elems+i) = *(src.elems+i); } return *this; } template