Trong C++ hàm rất quan trong nhưng nó sử dụng biến thế nào ta đến với bài hôm nay :
Xét một ví dụ :
int cong(int a,int b){return (a+b)};
//trong hàm chính :
int main(){
int a = 10;
int b = 5;
cout << "Tong cua a va b la: " << cong(a,b);
return 0;
}
Trên đây là một ví dụ rất đơn giản về hàm , chúng ta cùng phân tích :
Khi gọi hàm cong(a,b); thực chất hàm cong làm việc với bản sao của a,b mà không làm việc trực
tiếp với a,b
Tức là khi gọi hàm a,b tạo ra một bản sao và đưa cho hàm làm việc (Tạo ra một ô nhớ mới và copy giá trị qua)
việc copy thế này người ta gọi là shadow copy. Trong C++ nó gọi hàm khởi tạo sao chép để copy giá
trị mới , tương tự thế này : Ten_class _a(a); (tạo giá trị copy của a là _a), nếu không có hàm sao
chép khởi tạo thì nó gọi hàm sao chép mặc định.
Để chứng minh bài này ta làm như sau :
void ham2(int b){
cout << &b;
}
int main(){
int b = 0;
cout << &b << endl;
ham2(b);
return 0;
}
-> kết quả cho 2 giá trị (địa chỉ hoàn toàn khác nhau).
Trong các bài toán sử dụng đến con trỏ(như swap by pointer) thì thực chất con trỏ đó cũng tạo ra
một ô nhớ mới để copy ĐỊA CHỈ qua (giống giá trị trong biến) , và đưa cho hàm sử dụng bản sao đó
Vậy cái thay đổi mà ta nhận được là giá trị của biến có địa chỉ là địa chỉ nằm trong con trỏ,
chứ không phải con trỏ.
Xét một số ví dụ :
void ham(int *p){
p = new int [5];
for(int i =0 ; i<5 ;i++){
cout << "Nhap phan tu " << i << " : ";
cin >> *(p+i); // tương đương với p[i], hay i[p];
}
}
int main(){
int *p;
ham(p);
delete[] p; // <- Sai
return 0;
}
+Lỗi sai ta gặp phai ở đây là lệnh delete[] p; không gây lỗi cho trình biên dịch , mà gây run-time
một số trình biên dịch cùi không phát hiện ra. Lỗi sai này là do :
Như ta đã phân tích ở trên hàm làm việc với bản sao của con trỏ(ta tạm gọi bản sao của p ở trên là _p) ,
sau đó trong hàm bộ nhớ được cấp cho _p , chứ không phải cho p , nhưng ở hàm main () ta lại
delete[] p; => sai.
vd2 :
void ham1(int *p, int b){
p = &b;
}
int main(){
int a = 2;
int *p = &a;
int b = 5;
cout << p << " " << *p << endl;
ham1(p,b);
cout << p << " " << *p;
return 0;
}
-> kết quả vẫn cho như cũ vì hàm dùng bản sao của con trỏ chứ có dùng trực tiếp con trỏ đâu mà
con trỏ thay đổi giá trị.
Vậy muốn thay đổi giá trị (địa chỉ bên trong) con trỏ ta làm thế nào ????
Tôi tham khảo ở condongcviet.com .
Cách 1 : dùng tham chiếu trong C++
void ham(int *&a)
{
a=new int[100];
}
void ham(int **&a)
{
a=new int*[100];
}
xin chú ý là * đứng trước &
Cách 2 : up level của * dùng con trỏ cấp cao hơn con trỏ hiện tại
#include <stdio.h>
#include <conio.h>
void ham(int **a)
{
*a=(int*)malloc(100*sizeof(int));
//a[0]=(int*)malloc(100*sizeof(int));
// 2 cach nay nhu nay
}
void main()
{
int *a;
ham(&a);
free(a);
}
Trước khi kết thúc bài này ta đến với một điều rất thú vị (học đến class)
ta đã biết trong class có hàm tạo sao chép dùng để copy cho đối tượng mới một số thuộc tính
cụ thể nào đó của đối tượng đã xác định từ trước:
class Tenclass{
private :
thuộc tính ....
public :
//hàm tạo sao chép :
Tenclass(const Tenclass & dt1){....}; // <- chú ý chỗ này nhé
}
//hàm main()
int main(){
Tenclass dt;
//làm việc với thuộc tính của đối tượng dt.
.....................
// copy đối tượng mới :
Tenclass newdt(dt1); //<- chú ý chỗ này nữa
return 0;
}
Tại sao hàm copy constructor không sử dụng tham biến luôn đi lại còn sử dụng tham chiếu :
Tenclass(const Tenclass dt1){....};// cách viết 2
qua bài học này ta có thể giải thích như sau:
Trong hàm main() ta có lệnh gọi : Tenclass newdt(dt1);
lệnh này gọi hàm tạo sao chép nhưng hàm tạo sao chép ày dùng bản sao của dt1 , vì thế dt1 phải
sao chép ra một đôi tượng mới là _dt1 để cho hàm tạo sao chép sử dụng , không may trong quá trình
tạo đối tượng mới _dt1 , để tạo nó thì lại phải gọi hàm tạo sao chép và có tham số đầu vào vẫn là
dt1 , ..... => tiếp tục như quá trình trên gây run-time.