Tài liệu hướng dẫn thực hành môn Phương pháp lập trình hướng đối tượng - Tuần 3: Hàm hủy và vấn đề con trỏ

Tác giả:  
Môn PPLTHĐT  
Nguyễn Minh Huy  
Phạm Minh Tuấn  
Cập nhật:  
Hướng dẫn thực hành tuần 3  
Trương Phước Lộc  
Trần Duy Quang  
Mục đích  
Tìm hiểu 3 vấn đề về con trỏ trong lập trình hướng đối tượng với hàm hủy, hàm dựng sao  
chép và toán tử gán, giới thiệu class template.  
Nội dung  
-
-
-
-
Hàm hủy và vấn đề con trỏ.  
Hàm dựng sao chép và vấn đề con trỏ.  
Toán tử gán và vấn đề con trỏ.  
Class Template.  
Yêu cầu  
Nắm vững những nội dung trình bày trong bài hướng dẫn thực hành số 2.  
Hướng dẫn thực hành tuần 3  
Hàm hủy và vấn đề con trỏ  
1. Hàm hủy và vấn đề con trỏ  
Xét ví dụ xây dựng lớp mảng các số nguyên IntegerArray.  
Bước 1: vào VC++, tạo project dạng Console Application.  
Bước 2: thêm vào project file IntegerArray.h và viết khai báo lớp IntegerArray như sau:  
class IntegerArray  
{
private:  
int  
int  
*m_pElement;  
m_iLength;  
public:  
IntegerArray(int iLength);  
int GetLength();  
int & ElementAt(int iIndex);  
int & operator [](int iIndex);  
};  
Bước 3: thêm vào project file IntegerArray.cpp và viết cài đặt lớp IntegerArray như sau:  
#include "iostream.h"  
#include "IntegerArray.h"  
IntegerArray::IntegerArray(int iLength)  
{
if (iLength < 0)  
{
cout << "Loi: chieu dai mang la so am.";  
return;  
}
m_iLength = iLength;  
m_pElement = new int[m_iLength];  
}
int IntegerArray::GetLength()  
{
return m_iLength;  
}
int & IntegerArray::ElementAt(int iIndex)  
{
if (iIndex < 0 || iIndex >= m_iLength)  
2
 
Hướng dẫn thực hành tuần 3  
cout << "Loi: truy xuat phan tu ngoai pham vi mang.";  
return m_pElement[iIndex];  
Hàm hủy và vấn đề con trỏ  
}
int & IntegerArray::operator [](int iIndex)  
{
return ElementAt(iIndex);  
}
Bước 4: thêm vào project file main.cpp và viết đoạn chương trình sử dụng lớp  
IntegerArray vừa tạo như sau:  
#include "IntegerArray.h"  
void main()  
{
IntegerArray a(3);  
a[0] = 0;  
a[1] = 1;  
a[2] = 2;  
a.ElementAt(1) = 3;  
a.ElementAt(2) = 4;  
for (int i = 0; i < a.GetLength(); i++)  
cout << a[i] << endl;  
}
Bước 5: biên dịch và chạy thử chương trình.  
Trong đoạn chương trình trên, sau khi kết thúc hàm main(), đối tượng a bị hủy, vùng nhớ  
mà m_pElement và m_iLength chiếm chỗ được giải phóng. Nhưng vùng nhớ cấp phát  
cho m_pElement lại không được giải phóng. Điều này gây ra rò rỉ bộ nhớ (memory leak),  
đặc biệt nghiêm trọng trong trường hợp vùng nhớ cấp phát cho m_pElement lớn. Để giải  
quyết vấn đề này, chúng ta phải viết lệnh giải phóng vùng nhớ cấp phát cho m_pElement  
trong hàm hủy của lớp IntegerArray.  
Trong file IntegerArray.h thêm vào khai báo hàm hủy cho lớp IntegerArray như sau:  
virtual ~IntegerArray();  
Trong file IntegerArray.cpp viết cài đặt cho hàm hủy như sau:  
IntegerArray::~IntegerArray()  
{
if (m_pElement != NULL)  
delete []m_pElement;  
3
Hướng dẫn thực hành tuần 3  
Hàm hủy và vấn đề con trỏ  
}
Như vậy, khi lớp đối tượng có thuộc tính kiểu con trỏ và cấp phát vùng nhớ thì phải  
xây dựng hàm hủy cho lớp đối tượng đó để giải phóng vùng nhớ đã cấp phát.  
4
Hướng dẫn thực hành tuần 3  
Hàm dựng sao chép và vấn đề con trỏ  
2. Hàm dựng sao chép và vấn đề con trỏ  
Khái niệm  
Hàm dựng sao chép (copy constructor) là hàm dựng khởi tạo đối tượng dựa trên một đối  
tượng có cùng kiểu.  
Các tính chất của hàm dựng sao chép:  
-
-
Hàm dựng sao chép có đầy đủ tính chất của một hàm dựng thông thường.  
Thường được dùng để tạo bản sao của đối tượng.  
-
class A  
{
Trong C++, hàm dựng sao chép của lớp A được khai báo như sau:  
public:  
A(const A &obj);  
};  
Bất kỳ lớp đối tượng nào cũng có hàm dựng sao chép. Trong trường hợp chúng ta không  
khai báo hàm dựng sao chép cho lớp đối tượng, hàm dựng sao chép mặc định (default  
copy constructor) sẽ được tự động được thêm vào.  
Ví dụ  
Để hiểu rõ hơn về hàm dựng sao chép, chúng ta chỉnh sửa lại hàm main() của ví dụ lớp  
IntegerArray bên trên như sau:  
void main()  
{
IntegerArray a(3);  
a[0] = 0;  
a[1] = 1;  
a[2] = 2;  
a.ElementAt(1) = 3;  
a.ElementAt(2) = 4;  
IntegerArray b(a); // Tao doi tuong b la ban sao cua doi tuong a.  
for (int i = 0; i < b.GetLength(); i++)  
cout << b[i] << endl;  
}
Biên dịch và chạy thử chương trình chúng ta nhận được thông báo lỗi ngay tại hàm hủy  
của lớp IntegerArray. Chuyện gì đã xảy ra???  
5
     
Hướng dẫn thực hành tuần 3  
Hàm dựng sao chép và vấn đề con trỏ  
Phân tích chương trình chúng ta thấy rằng lớp IntegerArray không khai báo hàm dựng  
sao chép, do đó hàm dựng sao chép mặc định sẽ được tự động thêm vào. Hàm dựng sao  
chép mặc định tạo bản sao từ đối tượng bằng cách sao chép từng thuộc tính của đối tượng  
đó, nhưng lại không sao chép vùng nhớ cấp phát cho những thuộc tính kiểu con trỏ. Như  
vậy m_pElement của đối tượng b và m_pElement của đối tượng a sẽ sử dụng chung một  
vùng nhớ. Khi kết thúc hàm main(), đối tượng b bị hủy trước, hàm hủy của b được gọi để  
hủy vùng nhớ cấp phát cho m_pElement của b. Kế đó đối tượng a bị hủy, hàm hủy của a  
cũng được gọi để hủy vùng nhớ cấp phát cho m_pElement của a. Nhưng bấy giờ vùng  
nhớ này đã bị hủy trước đó. Chính điều này gây ra lỗi chương trình.  
Hơn nữa việc 2 mảng sử dụng chung một vùng nhớ để lưu các phần tử rõ ràng là sẽ gây  
ra những sai lệch về mặt ngữ nghĩa của chương trình.  
Để giải quyết vấn đề này, chúng ta phải xây dựng hàm tạo sao chép cho lớp IntegerArray.  
Trong file IntegerArray.h, thêm vào khai báo hàm dựng sao chép cho lớp IntegerArray  
như sau:  
IntegerArray(const IntegerArray &obj);  
Trong file IntegerArray.cpp viết cài đặt cho hàm dựng sao chép  
IntegerArray::IntegerArray(const IntegerArray &obj)  
{
m_iLength = obj.m_iLength;  
m_pElement = new int[m_iLength];  
// Sao chep vung nho da cap phat cho m_pElement cua obj.  
for (int i = 0; i < m_iLength; i++)  
m_pElement[i] = obj.m_pElement[i];  
}
Như vậy, khi lớp đối tượng có thuộc tính kiểu con trỏ và cấp phát vùng nhớ thì phải  
xây dựng hàm dựng sao chép cho lớp đối tượng đó để sao chép vùng nhớ đã cấp phát.  
6
Hướng dẫn thực hành tuần 3  
Toán tử gán và vấn đề con trỏ  
3. Toán tử gán và vấn đề con trỏ  
Vẫn sử dụng lớp IntegerArray xây dựng ở trên, chúng ta chỉnh sửa lại hàm main() như  
sau:  
void main()  
{
IntegerArray a(3);  
a[0] = 0;  
a[1] = 1;  
a[2] = 2;  
a.ElementAt(1) = 3;  
a.ElementAt(2) = 4;  
IntegerArray b(3);  
b = a; // Tao ban sao tu doi tuong a bang toan tu gan.  
for (int i = 0; i < b.GetLength(); i++)  
cout << b[i] << endl;  
}
Biên dịch và chạy thử chương trình chúng ta nhận được thông báo lỗi ngay tại hàm hủy  
của lớp IntegerArray. Chuyện gì đã xảy ra???  
Phân tích toán tử gán chúng ta thấy rằng khi gán đối tượng x bằng đối tượng y (x, y cùng  
kiểu) thì tất cả các thuộc tính của x sẽ được gán giá trị bằng các thuộc tính tương ứng của  
y. Điều này có nghĩa là trong ví dụ trên m_pElement của đối tượng b sẽ được gán giá trị  
bằng m_pElement của đối tượng a. Như vậy m_pElement của b và m_pElement của a sẽ  
cùng trỏ đến một vùng nhớ. Đây chính là vấn đề chúng ta đã gặp phải khi đề cập đến hàm  
tạo sao chép ở trên.  
Để giải quyết vấn đề này, chúng ta phải định nghĩa lại toán tử gán cho lớp IntegerArray.  
Trong file IntegerArray.h, thêm vào khai báo toán tử gán cho lớp IntegerArray như sau:  
IntegerArray & operator =(const IntegerArray &obj);  
Trong file IntegerArray.cpp viết cài đặt cho hàm dựng sao chép  
IntegerArray & IntegerArray::operator =(const IntegerArray &obj)  
{
m_iLength = obj.m_iLength;  
m_pElement = new int[m_iLength];  
// Sao chep vung nho da cap phat cho m_pElement cua obj.  
for (int i = 0; i < m_iLength; i++)  
7
 
Hướng dẫn thực hành tuần 3  
m_pElement[i] = obj.m_pElement[i];  
return *this;  
Toán tử gán và vấn đề con trỏ  
}
Như vậy, khi lớp đối tượng có thuộc tính kiểu con trỏ và cấp phát vùng nhớ thì phải  
định nghĩa lại toán tử gán cho lớp đối tượng đó để sao chép vùng nhớ đã cấp phát.  
8
Hướng dẫn thực hành tuần 3  
Class Template  
4. Class Template  
Khái niệm  
Xét ví dụ xây dựng lớp mảng cho nhiều kiểu dữ liệu khác nhau.  
// Mang so nguyen.  
class IntegerArray  
{
private:  
int  
*m_pElement;  
// Viet cai dat cho lop.  
};  
// Mang so thuc.  
class FloatArray  
{
private:  
float *m_pElement;  
// Viet cai dat cho lop.  
};  
// Mang ky tu.  
class CharArray  
{
private:  
char *m_pElement;  
// Viet cai dat cho lop.  
};  
Với mỗi kiểu dữ liệu khác nhau (int, float, char…) chúng ta phải xây dựng lớp mảng  
tương ứng cho kiểu dữ liệu đó. Điều này gây ra sự dư thừa không đáng có trong chương  
trình. Và hơn nữa các lớp trên vẫn không đủ dùng trong mọi trường hợp.  
=> Dùng Class Template.  
Class Template là lớp đối tượng tổng quát cho phép dùng nhiều kiểu dữ liệu khác nhau  
cho các thuộc tính và phương thức của lớp. Tương tự như Function Template, Class  
Template được khai báo bắt đầu bằng từ khóa “template”.  
template <class T>  
class SampleClass  
{
// Viet cai dat.  
};  
9
   
Hướng dẫn thực hành tuần 3  
Class Template  
Ví dụ  
Để hiểu rõ hơn về Class Template, chúng ta xét ví dụ xây dựng lớp mảng cho nhiều kiểu  
dữ liệu khác nhau Array.  
Bước 1: vào VC++, tạo project dạng Console Application.  
Bước 2: thêm vào project file Array.h, viết khai báo và cài đặt cho lớp Array như sau:  
template <class T>  
class Array  
{
private:  
T
int  
*m_pElement;  
m_iLength;  
public:  
Array(int iLength)  
{
if (iLength < 0)  
{
cout << "Loi: chieu dai mang la so am.";  
return;  
}
m_iLength = iLength;  
m_pElement = new T[m_iLength];  
}
Array(const Array &obj)  
{
m_iLength = obj.m_iLength;  
m_pElement = new T[m_iLength];  
// Sao chep vung nho da cap phat cho m_pElement cua obj.  
for (int i = 0; i < m_iLength; i++)  
m_pElement[i] = obj.m_pElement[i];  
}
int GetLength()  
{
return m_iLength;  
}
T & ElementAt(int iIndex)  
{
if (iIndex < 0 || iIndex >= m_iLength)  
cout << "Loi: truy xuat phan tu ngoai pham vi mang.";  
10  
 
Hướng dẫn thực hành tuần 3  
Class Template  
return m_pElement[iIndex];  
}
T & operator [](int iIndex)  
{
return ElementAt(iIndex);  
}
Array & operator =(const Array &obj)  
{
m_iLength = obj.m_iLength;  
m_pElement = new T[m_iLength];  
// Sao chep vung nho da cap phat cho m_pElement cua obj.  
for (int i = 0; i < m_iLength; i++)  
m_pElement[i] = obj.m_pElement[i];  
return *this;  
}
virtual ~Array()  
{
if (m_pElement != NULL)  
delete []m_pElement;  
}
};  
Bước 4: thêm vào project file main.cpp và viết đoạn chương trình sử dụng lớp Array vừa  
tao như sau:  
#include "Array.h"  
void main()  
{
Array<int> a(3);  
a[0] = 0;  
a[1] = 1;  
a[2] = 2;  
for (int i = 0; i < a.GetLength(); i++)  
cout << a[i] << endl;  
}
Bước 5: biên dịch và chạy thử chương trình.  
11  
Hướng dẫn thực hành tuần 3  
Class Template  
Bài tập  
Áp dụng class template xây dựng lớp mảng cho kiểu T (int, float, PhanSo, SoPhuc, …)  
có các chức năng cơ bản sau:  
-
-
-
-
Nhập, xuất mảng (toán tử >>, <<).  
Truy xuất đến phần tử nào đó trong mảng (toán tử [ ]).  
Tìm phần tử trong mảng.  
Sắp xếp mảng.  
12  
 
pdf 13 trang myanh 19700 Free
Bạn đang xem tài liệu "Tài liệu hướng dẫn thực hành môn Phương pháp lập trình hướng đối tượng - Tuần 3: Hàm hủy và vấn đề con trỏ", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdftai_lieu_huong_dan_thuc_hanh_mon_phuong_phap_lap_trinh_huong.pdf