Lập trình

Lập trình

Thứ Sáu, 13 tháng 6, 2014

Hàm tạo sao chép (Copy Constructor) : Định nghĩa , cách thức hoạt động cơ bản


Trong luc lập trình với lớp rất nhiều khi ta cần tạo một đối tượng mới có dữ liệu giống với những
đối tượng đã được thành lập từ trước , để giải quyết vấn đề này c++ có cách thức : Hàm tạo sao chép (Copy Constructor)

Ta đã có những cách khai báo đối tượng như sau :
Ten_class dt1,dt2;
TenClass * p = new TenClass;

Và đây là cách khai báo đối tượng theo kiểu Hàm tạo sao chép (Copy Constructor) :
TenClass dt3; // Tạo đối tượng dt3.
TenClass dt4(dt3); // Tạo dt4 theo dt3

Khi gặp dòng trên chương trình tạo ra một đối tượng dt4 ,cấp phát bộ nhớ cho nó kiểu TenClass
và sao chép toàn bộ dữ liệu(từng bit) của dt3 vào dt4 , người ta gọi đó là hàm tạo sao chép mặc định
. Ta thấy trong đa số các trường hợp (không có con trỏ và tham chiếu ) thì chỉ cần hàm sao chép
mặc định là đủ . Nhưng thực tế có rất nhiều những trường hợp có dữ liệu kiểu
 con trỏ (mảng,cấp phát động,...) ,tham chiếu thì ta không thể dùng cách này
(trường hợp này nói sau) .

Cách xây dựng hàm tạo sao chép:
Hàm tạo sao chép sử dụng đối kiểu tham chiếu để khởi gán cho đối tượng mới :

Ten_class(Ten_class & dt1){
    ...... // Những lệnh làm việc với thuộc tính dt1 để tạo đối tượng mới.
    }

Ta thấy khi không có con trỏ và tham chiếu chỉ cần hàm mặc định là đủ (không cần khai báo)
chỉ cần : TenClass dt1(dt); //dt đã được làm việc qua.

Nhưng khi thuộc tính có kiểu con trỏ , nếu ta dùng hàm mặc định khi đó 2 thuộc tính con trỏ
của 2 đối tượng trên sẽ cùng trỏ tới một ô nhớ khi đó nếu thay đổi con trỏ của đối tượng này thì con trỏ
của đối tượng kia sẽ thay đổi , thậm chí khi ta xóa (delete) vùng nhớ của con trỏ này thì cũng
đồng nghĩa con trỏ kia cũng bị xóa (NULL)

Như vậy chỉ dùng hàm tạo sao chép mặc định thôi là chưa đủ .

Ta cùng đến với một vài ví dụ :

// Hàm tao sao chép
#include<iostream>
#include<string.h>

using namespace std;

class SinhVien{
private :
int tuoi;
char* ten;
int len;// do dai ten
public :
void setname(char name[]){
strcpy(ten,name);
}
char * getname(){
return ten;
}
void settuoi(int tuoi){
this->tuoi = tuoi;
}
int gettuoi(){
return tuoi;
}
//Ham tao
  SinhVien(int len){
  this->len = len;
  ten = new char[len+1];//nen luon de dong cap phat bo nho nay
                     // trong ham khoi tao
  }
  //ham tao sao chep
  SinhVien(SinhVien & dt){
  this->len = dt.len;
  this->ten = new char[dt.len+1]; // Phai cap phat bo nho truoc
  this->tuoi = dt.tuoi;//thuoc tinh tuoi cua doi tuong dang duoc khai bao
                      // se co thuoc tinh tuoi giong thuoc tinh tuoi cua dt;
  for(int i=0 ;i<len;i++){
  (*this).ten[i] = dt.ten[i];
  }               
  }
  void show();// Xuat thong tin cua sinh vien
};
void SinhVien :: show(){
cout << "Ten la: " << ten << endl;
cout << "Tuoi la: " << tuoi << endl;
}
int main(){
//cout << "Do dai cua ho ten la: ";
//int len;
//cin >> len;
char ten[] = "Nguyen Van a";
SinhVien sv(strlen(ten));
sv.setname(ten);
sv.settuoi(17);
cout << "Thong tin cua sinh vien thu nhat: " << endl;
sv.show();
SinhVien sv2(sv); // tim den ham tao copy co doi truyen vao.
cout << "Thong tin cua sinh vien thu hai : " << endl;
sv2.show();
return 0;
}

Kết Quả :

Ở đây 2 con trỏ của 2 đôi tượng không trỏ cùng vào một vùng nhớ vì thế các bạn có thể delete[] thuộc tính tên của đối tượng bất kì và kiểm tra đối tượng còn lại
Ví dụ ở đây tôi thêm hamhuy(tôi chỉ ví dụ thôi chứ đây không phải hàm hủy thực sự.) :
void hamhuy(){
  delete[] ten;
  }
 
Thêm vào trong class SinhVien (public) .
Sau đó code lại :
  
// Hàm tao sao chép
#include<iostream>
#include<string.h>

using namespace std;

class SinhVien{
private :
int tuoi;
char* ten;
int len;// do dai ten
public :
void setname(char name[]){
strcpy(ten,name);
}
char * getname(){
return ten;
}
void settuoi(int tuoi){
this->tuoi = tuoi;
}
int gettuoi(){
return tuoi;
}
//Ham tao
  SinhVien(int len){
  this->len = len;
  ten = new char[len+1];//nen luon de dong cap phat bo nho nay
                     // trong ham khoi tao
  }
  //ham tao sao chep
  SinhVien(SinhVien & dt){
  this->len = dt.len;
  this->ten = new char[dt.len+1]; // Phai cap phat bo nho truoc
  this->tuoi = dt.tuoi;//thuoc tinh tuoi cua doi tuong dang duoc khai bao
                      // se co thuoc tinh tuoi giong thuoc tinh tuoi cua dt;
  for(int i=0 ;i<len;i++){
  (*this).ten[i] = dt.ten[i];
  }               
  }
  void show();// Xuat thong tin cua sinh vien
  void hamhuy(){
  delete[] ten;
  }
};
void SinhVien :: show(){
cout << "Ten la: " << ten << endl;
cout << "Tuoi la: " << tuoi << endl;
}
int main(){
//cout << "Do dai cua ho ten la: ";
//int len;
//cin >> len;
char ten[] = "Nguyen Van a";
SinhVien sv(strlen(ten));
sv.setname(ten);
sv.settuoi(17);
cout << "Thong tin cua sinh vien thu nhat: " << endl;
sv.show();
SinhVien sv2(sv); // tim den ham tao copy co doi truyen vao.
sv.hamhuy(); // Khong anh huong
cout << "Thong tin cua sinh vien thu hai : " << endl;
sv2.show();
return 0;
}
Ta thấy thuôc tình tên của đối tượng thứ 2 không bị mất đi trong đó thuộc tính của đối tượng một bị mất đi.


Nếu bạn muốn thấy vi dụ về việc hàm sao chép mặc định gây ra lỗi hai con trỏ vào cùng một vùng nhớ thì làm như sau :
 Bỏ cái hàm tạo sao chép do mình tạo nên tức là lúc này sử dụng hàm tạo mặc định của chương trình :
// Hàm tao sao chép
#include<iostream>
#include<string.h>

using namespace std;

class SinhVien{
private :
int tuoi;
char* ten;
int len;// do dai ten
public :
void setname(char name[]){
strcpy(ten,name);
}
char * getname(){
return ten;
}
void settuoi(int tuoi){
this->tuoi = tuoi;
}
int gettuoi(){
return tuoi;
}
//Ham tao
  SinhVien(int len){
  this->len = len;
  ten = new char[len+1];//nen luon de dong cap phat bo nho nay
                     // trong ham khoi tao
  }
  //ham tao sao chep
  /*SinhVien(SinhVien & dt){
  this->len = dt.len;
  this->ten = new char[dt.len+1]; // Phai cap phat bo nho truoc
  this->tuoi = dt.tuoi;//thuoc tinh tuoi cua doi tuong dang duoc khai bao
                      // se co thuoc tinh tuoi giong thuoc tinh tuoi cua dt;
  for(int i=0 ;i<len;i++){
  (*this).ten[i] = dt.ten[i];
  }               
  }*/
  void show();// Xuat thong tin cua sinh vien
  void hamhuy(){
  delete[] ten;
  }
};
void SinhVien :: show(){
cout << "Ten la: " << ten << endl;
cout << "Tuoi la: " << tuoi << endl;
}
int main(){
//cout << "Do dai cua ho ten la: ";
//int len;
//cin >> len;
char ten[] = "Nguyen Van a";
SinhVien sv(strlen(ten));
sv.setname(ten);
sv.settuoi(17);
cout << "Thong tin cua sinh vien thu nhat: " << endl;
sv.show();
SinhVien sv2(sv); // tim den ham tao copy co doi truyen vao.
sv.hamhuy(); // Khong anh huong
cout << "Thong tin cua sinh vien thu hai : " << endl;
sv2.show();
return 0;
}

Kết quả :

 
 chỗ bị bôi vàng là do ta đã xóa con trỏ của đối tượng một nên đối tượng hai có con trỏ NULL
 

Không có nhận xét nào:

Đăng nhận xét