Lập trình hướng đối tượng - Sự khởi tạo và hủy
Một sự ngầm định constructor có thể được gọi là không có đối số. Một
constructor mặc định được sử dụng để tạo ra một đối tượng “vanilla”, nhưng nó
cũng rất quan trọng khi trình biên dịch được nói để tạo ra một đối tượng nhưng
không đưa ra bất kỳ chi tiết nào. Ví dụ, nếu bạn đi struct Y được xác định trước
đó và sử dụng nó trong một định nghĩa như thế này:
Y y2[2] = { Y(1) };
Trình biên dịch sẽ than phiền rằng nó không thể tìm thấy một constructor
ngầm định. Đối tượng thứ hai trong mảng đó muốn được tạo ra không có đối số,
và đó là nơi trình biên dịch sẽ cho một constructor ngầm định. Trong thực tế, nếu
bạn chỉ cần định nghĩa một mảng các đối tượng Y,
nhiệm. Ví dụ, nếu bạn không pop () và xóa tất cả các con trỏ trên Stack, họ sẽ
không nhận được làm sạch tự động bởi destructor của Stack. Điều này có thể là
một vấn đề dính và dẫn đến rò rỉ bộ nhớ, để biết những người có trách nhiệm làm
sạch lên một đối tượng có thể làm cho sự khác biệt giữa một chương trình thành
công và là một trong xe đẩy - đó là lý do tại sao Stack:: ~ Stack () in một thông
báo lỗi nếu Stack đối tượng là không có sản phẩm nào khi tiêu hủy.
Bởi vì việc cấp phát và hủy của các đối tượng liên kết được ẩn trong Stack
- nó là một phần của việc thực hiện ngầm- bạn không thấy nó xảy ra trong
chương trình thử nghiệm, mặc dù bạn có trách nhiệm xóa các con trỏ mà quay lại
từ cửa sổ pop ():
#include "Stack3.h"
#include "../require.h"
#include
#include
#include
using namespace std;
int main(int argc, char* argv[]) {
requireArgs(argc, 1); // File name is argument
ifstream in(argv[1]);
assure(in, argv[1]);
Stack textlines;
string line;
// Read file and store lines in the stack:
while(getline(in, line))
textlines.push(new string(line));
// Pop the lines from the stack and print them:
string* s;
while((s = (string*)textlines.pop()) != 0) {
cout << *s << endl;
delete s;
}
} ///:~
Trong trường hợp này, tất cả các dòng textlines đã được lấy và xóa bỏ,
nhưng nếu chúng không được, bạn sẽ nhận được một require () thông báo rằng sẽ
có nghĩa là có một sự rò rỉ bộ nhớ.
VI .Khởi tạo tập hợp
Một tập hợp giống như là : một nhóm những khối với nhau. Định nghĩa
này bao gồm tập hợp các loại hỗn hợp, như cấu trúc và các lớp học. Một mảng là
một tổng hợp của một loại duy nhất.
Việc khởi tạo tập hợp có thể dễ bị lỗi và tẻ nhạt. C + +, khởi tạo tập hợp
làm cho nó an toàn hơn nhiều. Khi bạn tạo một đối tượng mà là một tập hợp, tất
cả những gì bạn phải làm là tạo một phân công, và việc khởi tạo sẽ được theo dõi
bởi trình biên dịch.Tùy thuộc vào loại hình tập hợp bạn đang xử lý, nhưng trong
tất cả các yếu tố trong trường hợp khai báo phải được bao quanh bởi các dấu
ngoặc nhọn. Đối với một mảng loại built-in này khá đơn giản:
int a[5] = { 1, 2, 3, 4, 5 };
Nếu bạn cố gắng cung cấp nhiều việc khởi tạo hơn, đó là những yếu tố
mảng, trình biên dịch cho một thông báo lỗi. Nhưng những gì xảy ra nếu bạn
cung cấp ít việc khởi tạo hơn? Ví dụ:
int b[6] = {0};
Ở đây, trình biên dịch sẽ sử dụng sự khởi tạo đầu tiên cho các phần tử
mảng đầu tiên, và sau đó sử dụng số 0 cho tất cả các yếu tố mà không được khởi
tạo. Thông báo khởi tạo hành vi này không xảy ra nếu bạn định nghĩa một mảng
mà không có một danh sách các phần tử bắt đầu. Vì vậy, các biểu hiện ở trên là
một cách gọn gàng để khởi tạo một mảng về không, mà không cần sử dụng cho
vòng lặp, và không có khả năng xảy ra một lỗi off-by-one (Tùy thuộc vào trình
biên dịch, nó cũng có thể hiệu quả hơn hơn cho vòng lặp. )
Một giây viết tắt cho mảng thứ hai là tự động đếm, mà trong đó bạn cho
phép trình biên dịch xác định kích thước của mảng đó dựa trên số lượng phần tử
khởi đầu:
int c[] = { 1, 2, 3, 4 };
Bây giờ nếu bạn quyết định thêm một yếu tố đến mảng đó, bạn chỉ cần
thêm một phần tử khởi đầu. Nếu bạn có thể đặt mã của bạn lên vì vậy nó cần phải
được thay đổi trong chỉ có một chỗ, bạn làm giảm cơ hội sai sót trong quá trình
sửa đổi. Nhưng làm thế nào để bạn xác định kích thước của mảng đó? Những
biểu hiện sizeof c sizeof * c (kích thước của toàn bộ mảng chia cho kích thước
của phần tử đầu tiên) như thế với một thủ thuật mà không cần phải được thay đổi
nếu các mảng thay đổi kích cỡ :
for(int i = 0; i < sizeof c / sizeof *c; i++)
c[i]++;
Bởi vì cấu trúc cũng được tập hợp, chúng có thể được khởi tạo trong một
phong cách tương tự. Bởi vì một C-style struct có tất cả các thành viên public
của mình, chúng có thể được gán giá trị trực tiếp:
struct X
{
int i;
float f;
char c;
};
X x1 = { 1, 2.2, 'c' };
Nếu bạn có một mảng các đối tượng như vậy, bạn có thể khởi tạo chúng
bằng cách sử dụng một bộ lồng nhau của các dấu ngoặc nhọn cho từng đối tượng:
X x2[3] = { {1, 1.1, 'a'}, {2, 2.2, 'b'} };
Ở đây, đối tượng thứ ba là khởi tạo là “không”.
Nếu bất kỳ thành viên dữ liệu là private (mà thường là trường hợp cho một
lớp được thiết kế tốt trong C + +), hoặc thậm chí nếu tất cả mọi thứ của public,
nhưng có một constructor, những thứ có khác nhau. Trong ví dụ trên, phần tử
khởi đầu được gán trực tiếp vào các phần tử của tập hợp, nhưng constructor là
một cách để buộc khởi tạo xảy ra một cách chính thức. Ở đây, các constructor
phải được gọi là thực hiện khởi t ạo. Vì vậy, nếu bạn có một cấu trúc trông như
thế này:
struct Y
{
float f;
int i;
Y(int a);
};
Bạn phải chỉ ra các cuộc gọi constructor. Phương pháp tốt nhất là rõ ràng
như sau:
Y y1[] = { Y(1), Y(2), Y(3) };
Bạn nhận ba đối tượng và ba lời gọi constructor. Bất cứ lúc nào bạn có
một constructor, cho dù đó là một cấu trúc với tất cả các thành viên công cộng
hoặc một lớp với các dữ liệu cá nhân thành viên, tất cả các khởi phải qua
constructor, thậm chí nếu bạn đang sử dụng khởi tạo tổng hợp.
Dưới đây là một ví dụ thứ hai về constructor nhiều tham số:
#include
using namespace std;
class Z
{
int i, j;
public:
Z(int ii, int jj);
void print();
};
Z::Z(int ii, int jj) {
i = ii;
j = jj;
}
void Z::print() {
cout << "i = " << i << ", j = " << j <<
endl;
}
int main() {
Z zz[] = { Z(1,2), Z(3,4), Z(5,6), Z(7,8)
};
for(int i = 0; i < sizeof zz / sizeof *zz;
i++)
zz[i].print();
} ///:~
Chú ý rằng nó giống như một constructor được gọi là rõ ràng cho từng đối
tượng trong mảng đó.
VII.Khởi tạo mặc định
Một sự ngầm định constructor có thể được gọi là không có đối số. Một
constructor mặc định được sử dụng để tạo ra một đối tượng “vanilla”, nhưng nó
cũng rất quan trọng khi trình biên dịch được nói để tạo ra một đối tượng nhưng
không đưa ra bất kỳ chi tiết nào. Ví dụ, nếu bạn đi struct Y được xác định trước
đó và sử dụng nó trong một định nghĩa như thế này:
Y y2[2] = { Y(1) };
Trình biên dịch sẽ than phiền rằng nó không thể tìm thấy một constructor
ngầm định. Đối tượng thứ hai trong mảng đó muốn được tạo ra không có đối số,
và đó là nơi trình biên dịch sẽ cho một constructor ngầm định. Trong thực tế, nếu
bạn chỉ cần định nghĩa một mảng các đối tượng Y,
Y y3[7];
Trình biên dịch sẽ than phiền vì nó phải có một constructor ngầm định để
khởi tạo mọi đối tượng trong mảng đó.
Cùng một vấn đề xảy ra nếu bạn tạo một đối tượng cá nhân như thế này:
Y y4;
Hãy nhớ rằng, nếu bạn có một constructor, trình biên dịch bảo đảm cho
việc khởi tạo luôn luôn xảy ra, bất kể tình hình.
Constructor ngầm định rất quan trọng đến nỗi nếu (và chỉ nếu) không có
constructor cho một cấu trúc (struct hay lớp), trình biên dịch sẽ tự động tạo ra
một cho bạn. Đây la công việc như vậy:
class V
{
int i; // private
}; // No constructor
int main()
{
V v, v2[10];
} ///:~
Nếu bất kỳ nhà thầu được định nghĩa, tuy nhiên, và không có constructor
mặc định, các trường hợp của V ở trên sẽ tạo ra biên dịch lỗi.
Bạn có thể nghĩ rằng trình biên dịch, tổng hợp construct nên làm một số
khởi tạo thông minh, như đặt tất cả các bộ nhớ cho đối tượng về không. Nhưng
nó không - mà có thể gắn thêm trên không nhưng có thể nằm ngoài kiểm soát của
lập trình viên. Nếu bạn muốn bộ nhớ để khởi tạo về không, bạn phải làm điều đó
cho mình bằng cách viết các constructor mặc định một cách rõ ràng.
Mặc dù trình biên dịch sẽ tạo ra một constructor mặc định cho bạn, hành vi
của trình biên dịch, tổng hợp construct là rất hiếm những gì bạn muốn. Bạn nên
xử lý tính năng này như là một mạng lưới an toàn, nhưng ít sử dụng nó. Nói
chung, bạn nên xác định nhà thầu của bạn một cách rõ ràng và không cho phép
trình biên dịch để làm điều đó cho bạn.
VIII.Tổng kết
- Những kĩ thuật tỉ mỉ của C++ đã gợi ý cho bạn về việc khởi tạo và hủy một
đối tượng một cách an toàn trong ngôn ngữ này.Theo Stroustrup, tác giả
của ngôn ngữ C++, đã tìm hiểu rất kĩ về C, để nhận thấy rằng C có rất
nhiều vấn đề trong việc khởi tạo và hủy đối tượng. Những lỗi này có thể
gây ra những hậu quả nghiêm trọng, rất khó nhận ra, kể cả việc khởi tạo và
hủy một biến. Constructor và destructor cho phép bạn kiểm soát an toàn
hơn.
- Việc khởi tạo và hủy còn bao gồm cả việc khởi tạo tập hợp, như tiếp thêm
nguồn cảm hứng. Nó ngăn bạn làm cho những sai lầm khi khởi tạo các tập
hợp và làm cho mã của bạn gọn gàng hơn.
Bởi vậy, có thể nói rằng, an toàn trong viết code là một kết quả lớn của
C++. Và như đã tìm hiểu ở trên, khởi tạo và hủy cũng an toàn như vậy.
IX. Bài tập
1. Viết một class, in ra một vài thứ, đẻ cho biết một hàm khởi tạo đã được
gọi.
2. Thêm vào một hàm hủy, trong đó có lệnh in ra cho biết hàm hủy đã được
gọi.
3. Sửa class đó, thêm vào một biến int, rồi viết hàm khởi tạo có tham số, rồi
gán cho biến đó.
4. Chứng minh rằng hàm hủy vẫn được gọi khi bị lệnh goto nhảy qua.
5. Viết ra hai vòng lặp for, vòng thứ nhất khai báo một biến đếm ở ngoài
vòng, vòng thứ hai khai báo một biến đếm ngay trong cấu trúc điều khiển
của vòng lặp, theo dõi trình biên dịch xử lí thế nào ?
6. Sử dụng khởi tạo tập hợp để tạo ra một mảng double nhưng không cung
cấp hết giá trị cho các phần tử. In ra phần tử của mảng, sử dụng sizeof để
tính kích thước mảng. Bây giờ tạo một mảng sử dụng khởi tạo tập hợp và
tính kích thước tự động.
7. Sử dụng khởi tạo mảng để tạo ra một mảng các chuỗi. Tạo ra một Stack
giữ các chuỗi này, sau đó dùng pop() để xuất các chuỗi này ra.
8. Tạo ra một class không có hàm khởi tạo, chứng minh rằng bạn có thể tạo
một class với hàm khởi tạo mặc định. Bây giờ tạo một hàm khởi tạo có
tham số, rồi thử lại. Giải thích vấn đề.
Hết
Nguyễn Công Huy – 08520148
Nguyễn Trung Kiên – 08520184
File đính kèm:
Lập trình hướng đối tượng - Sự khởi tạo và hủy.pdf

