Tóm Tắt
1. Copy Constructor là gì ?
a. Giới thiệu Copy Constructor
Copy Constructor là một Constructor đặc biệt quan trọng, nó được dùng để tạo một bản sao của một đối tượng người dùng đã có trước đó. Copy Constructor có tham số là địa chỉ vùng nhớ tham chiếu đến đối tượng người tiêu dùng cần sao chép .
Trong hình minh họa trên, Ban đầu chúng ta có đối tượng A có địa chỉ vùng nhớ là 0x0100, nó là một hình tròn màu vàng viền đen. Do nhu cầu nên chúng ta muốn tạo ra một bản sao giống hệt Đối tượng A và đặt tên là Đối tượng B có địa chỉ vùng nhớ 0x0300. Vai trò copy constructor trong ví dụ trên chỉ có vậy.
b. Vì sao phải dùng Copy Constructor ?
Sau khi đọc xong phần trình làng có lẽ rằng bạn đặt khá nhiều câu hỏi, vai trò chỉ là sao chép thì tại sao lại nói là nó quan trọng, trong khi phép gán đã xử lý được yếu tố sao chép ?Như bạn cũng biết, Pointer ( Con trỏ ) trong C + + lưu địa chỉ vùng nhớ, chính về thế, nếu bạn muốn sao chép nội dung 2 pointer trỏ đến bằng phép gán b = a thì lúc này nó chỉ gán địa chỉ vùng nhớ của a cho b mà thôi, và nội dung của 2 là một, nên khi bạn tác động ảnh hưởng lên tài liệu mà a hoặc b trỏ đến thì cả nội dung của a và b đều sẽ đổi khác. Nên giải pháp gán này không hài hòa và hợp lý .Trong C + + khi bạn truyền vào hàm, thủ tục một đối tượng người tiêu dùng, thì nó sẽ gọi copy constructor của kiểu tài liệu đó để triển khai 1 số sao chép. Nếu kiểu tài liệu đó không có copy constructor thì nó sẽ tự gán giá trị của biến đó cho một biến khác. Ví dụ
1234567 |
intm=1,n=2; intmax(inta,intb) { if(a>b)returna; returnb; } |
Trong ví dụ trên khi bạn gọi max ( m, n ) ở đây bạn truyền vào tham trị nó sẽ gọi copy constructor của class số int và bạn hiểu là phương pháp đó thực thi sao chép tài liệu để nếu bạn có đổi khác giá trị a b ở trong hàm thì 2 biến m, n ở ngoài truyền vào sẽ không ảnh hưởng tác động .Sau khi xử lí xong, và thoát khỏi hàm, nó sẽ gọi hàm destructor để hủy những biến mà nó tạo ra, trong trường hợp này gồm có những biến được tạo bằng constructor và Copy constructor. Phần về Destructor mình sẽ nói bên dưới .Như vậy nếu đối tượng người tiêu dùng của mình có tài liệu là một pointer cấp phát động, ví dụ điển hình mình sẽ tạo ra một class chứa mảng số nguyên như sau :
1234567891011121314151617181920212223242526272829303132333435363738 |
classMangInt { private: int*A; intn; public: voidxuat() { |
Ở ví dụ trên khi khởi tạo đối tượng mình sẽ cho nó cấp phát động mảng A, và theo nguyên tắc thông thường, có cấp phát động phải có thu hồi bộ nhớ, nên mình sẽ thu hồi bộ nhớ ở destructor, và bây giờ mình khai báo MangInt Array1; sau đó truyền Array1 vào hàm vidu
Như miêu tả bên trên nó sẽ gọi copy constructor nhưng mình chưa khai báo cho nó nên mặc định nó sẽ làm là
– Gán Array1. n cho array. n– Gán Array1. A cho array. A ( quan tâm đây là pointer nên nó chép địa chỉ )
Sau khi xử lí xong hàm, lúc thoát khỏi hàm nó sẽ gọi Destructor cho những gì nó tạo khi chạy hàm đó. Như vậy bao gồm nó sẽ gọi destructor của array (trong đây nó thu hồi bộ nhớ của array)
Như vậy sau khi chạy xong hàm vidu trong main() thì nó sẽ kết thúc hàm main và vẫn theo nguyên tắc cũ là gọi Destructor cho những gì nó đã tạo trong hàm đó. Lúc này Array1 được gọi hủy. Theo như trong code hàm hủy, mình delete pointer A, mà địa chỉ pointer A đó đã bị hủy trước đó trong hàm vidu, nên lúc này xảy ra hiện tượng lỗi thu hồi 2 lần trên cùng một vùng nhớ.
Chính do đó mà vai trò Copy Constructor là khi nó được chạy thì nó sẽ tạo ra vùng nhớ mới bằng cách cấp phát động và gán giá trị qua chứ không phải gán địa chỉ của 2 pointer theo cách mặc định của c + +
2. Cài đặt Copy Constructor như thế nào ?
“Bài viết này mình nói khá dài dòng, nhưng bạn chỉ cần nhớ một câu duy nhất Khi nào dữ liệu của class có cấp phát động, hãy cài đặt Copy Constructor”
a. Khai báo copy constructor
12345 |
Tên_Class(constTên_Classvàtenbien) { / / cấp phát động cho tài liệu có con trỏ / / gán những tài liệu của tên biến cho đối tượng người dùng hiện tại } |
b. Ví dụ về khai báo copy constructor
Mình sẽ lấy ví dụ về MangInt bên trên để viết copy constructor cho nó
1234567 |
MangInt(constMangIntvàmang) { n=mang.n;/ / gán n của mang qua n của đối tượng người tiêu dùng cần sao chép A=newint[n];/ / cấp phát động lại vùng nhớ mới for(inti=0;i A[i]=mang.A[i];/ / sao chép hết giá trị từng thành phần qua vung nhớ mới } |
3. Bài tâp ứng dụng Copy Constructor
a. Đề bài
Viết định nghĩa lớp String để biểu diễn khái niệm chuỗi ký tự với các phương thức thiết lập và huỷ bỏ, các hàm thành phần tính chiều dài chuỗi, so sánh hai chuỗi, nối hai chuỗi, đảo chuỗi, xuất chuỗi.
Lưu ý: không được sử dụng kiểu string của C++ (của thư viện chuẩn
Ở bài này theo nhu yếu bạn nên tạo một class có 2 dữ liệu là poiner bộc lộ mảng kí tự char và một biến số nguyên lưu độ dài xâu .
b. Code lời giải
Bài này mình đã làm quá lâu rồi, nên một số ít chỗ chưa hài hòa và hợp lý, tuy nhiên những bạn hoàn toàn có thể tìm hiểu thêm để hiểu về cách ứng dụng Copy Constructor và viết lại theo cách riêng của mình .
String. h
123456789101112131415161718 |
# pragma once classString { private: char*pString; intDoDai; public: String(); String(constchar*str); String(constStringvàa);/ / copy constructor ~String(); intGetLength(); voidxuat(); intSoSanh(constStringvàa); voidNoiChuoi(constStringvàa); voidnhap(); voidDaoChuoi(); }; |
String. cpp
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
#include # include ” String. h “
#include usingnamespacestd; String::String() { DoDai=1; pString=NULL; } String::String ( constchar*str) { DoDai=strlen(str)+1; pString=newchar[DoDai]; strcpy_s(pString,DoDai,str); pString[DoDai-1]=’ \ 0 ‘; } String::String(constStringvàa) { DoDai=a.DoDai; pString=newchar[DoDai]; strcpy_s(pString,DoDai,a.pString); } String::~String() { delete[]pString; } intString::GetLength() { returnDoDai-1; } voidString::xuat() { if(pString! =NULL) cout< } intString::SoSanh(constStringvàa) { if(a.pString= =NULLvà vàpString= =NULL) return0; if(a.pString= =NULL) return1; if(pString= =NULL) return-1; returnstrcmp(pString,a.pString); } voidString::NoiChuoi(constStringvàa) { if(a.pString= =NULLvà vàpString= =NULL) return; if(a.pString= =NULL) return; char*p=newchar[DoDai+a.DoDai-1]; if(pString= =NULL) { strcpy_s(p,a.DoDai,a.pString); delete[]pString; pString=p; return; } for(inti=0;i< =DoDai-2;i+ +) p[i]=pString[i]; for(inti=0;i p[i+DoDai-1]=a.pString[i]; delete[]pString; pString=p; DoDai=DoDai+a.DoDai-1; } voidString::nhap() { cin>>pString; DoDai=strlen(pString); } voidString::DaoChuoi() { _strrev(pString); } |
Main. cpp
1234567891011121314151617 |
#include # include ” String. h “ usingnamespacestd; intmain() { Stringa(” con vit “),b(” con ga “); a.NoiChuoi(b); cout< <" length = "< a.xuat(); a.DaoChuoi(); cout< a.xuat(); system(” pause “); return0;
} |
Sau khi giải xong bài này bạn sẽ đặt hỏi vì sao khi khai báo kiểu String a = ” con vit “, b = ” con ga ” ; bị sai hiệu quả hay vì sao không hề gọi ghép string bằng a + b ví dụ điển hình, thì nó cần đến overloading operator để tạo lại toán tử, cả phép gán = cũng hoàn toàn có thể overloading lại, nên khi bạn không định nghĩa toán tử này mà sử dụng nó sẽ cho hiệu quả không mong ước. Ở những bài sau mình sẽ nói về overloading operator =
Bạn có thể làm thêm các bài tập dưới đây:
Bài tập 3 – Bài này mình giải có sai sót, bạn vui mừng làm lại và ứng dụng copy constructor .
Source: https://final-blade.com
Category: Kiến thức Internet