Trình bày chương 6 trong cuốn Thinking in C++ Volum 1

Chức năng của hàm khởi tạo:

1. Khởi tạo bộ nhớ đúng cách.

2. Constructor đối số cung cấp cho bạn một

cách để bảo đảm rằng tất cả các phần của

đối tượng của bạn được khởi tạo các giá trị

thích hợp. Tránh việc trả về các giá trị

không mong muốn.

ví dụ: Tree t(12); // 12-foot tree

pdf5 trang | Chuyên mục: Lập Trình Hướng Đối Tượng | Chia sẻ: dkS00TYs | Lượt xem: 1441 | Lượt tải: 0download
Tóm tắt nội dung Trình bày chương 6 trong cuốn Thinking in C++ Volum 1, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
= 0; i < oldBytes; i++) 
b[i] = storage[i]; // Copy old to new 
delete [](storage); // Old storage 
storage = b; // Point to new memory 
quantity = newQuantity; 
} 
Stash::~Stash() { 
if(storage != 0) { 
cout << "freeing storage" << endl; 
delete []storage; 
} 
} ///:~ 
Stash với constructors và destructor
Stash with constructors and destructor
 Bạn ó thể thấy rằng các chức năng
require.h đang được sử dụng để xem
lỗi lập trình, thay vì assert( ). Đầu ra
của một thất bại assert( ).không phải
là hữu ích như là của các chức năng
require.h
Stash với constructors và destructor
Stash with constructors and destructor
//: C06:Stash2Test.cpp 
//{L} Stash2 
// Constru tors & 
destructors 
#include "Stash2.h" 
#include "../require.h" 
#include 
#include 
#include 
int main() { 
Stash intStash(siz of(int)); 
for(int i = 0; i < 100; i++) 
intStash.add(&i); 
for(int j = 0; j < intStash.count(); j++)
cout << "intStash.fetch(" << j << ") = " 
<< *(int*)intStash.fetch(j) 
<< endl; 
const int bufsize = 80; 
Stash stringStash(sizeof(char) * bufsize); 
ifstream in("Stash2Test.cpp"); 
ass re(in, " Stash2Test.cpp"); 
string line; 
while(getline(in, line)) 
stringStash.add((char*)line.c_str()); 
int k = 0; 
char* cp; 
while((cp = (char*)stringStash.fetch(k++))!=0) 
cout << "stringStash.fetch(" << k << ") = " 
<< cp << endl; 
} ///:~
Stack với constructors và destructor
Stack with constructors and destructor
 Các dan sách liên kết (bên trong Stack) với
constructor và destructors cho thấy cách gọn gàng
constructor và làm việc với destructors mới và xóa. 
Đây là tiêu đề sửa đổi tập tin:
//: C06:Stack3.h
// With constructors/destructors
#ifndef STACK3_H 
#define STACK3_H 
class Stack { 
struct Link { 
void* data; 
Link* next; 
Link(void* dat, Link* nxt); 
~Link(); 
}* head; 
public: 
Stack(); 
~Stack(); 
void push(void* dat); 
void* peek(); 
void* pop(); 
}; 
Stack với constructors và destructor
Stack with constructors and destructor
 Không những Stack có một constructor và destructor, 
mà cò kết hợp lớp Link:
//: C06:Stack3.cpp {O}
// Constructors/de tructors
#include "Stack3.h"
#include "../require.h"
using namespace std;
Stack::Link::Link(void* dat, Link* 
nxt) {
data = dat;
next = nxt;
}
Stack::Link::~Link() { }
Stack::Stack() { head = 0; }
void Stack::push(void* dat) {
head = new Link(dat,head);
}
void* Stack::peek() {
require(head != 0, "Stack empty");
return head->data;
}
void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}
Stack::~Stack() {
require(head == 0, "Sta k not 
empty");
} ///:~
Stack với constructors và destructor
Stack with constructors and destructor
 Link::Link( ) nstructor chỉ đơn giản khởi
data và con trỏ next, vì vậy trong
Stack::push( ) có dòng
Line head = new Link(dat,head); 
không những phân bổ một liên kết mới (sử
dụng đối tượng năng động, sáng tạo với các
từ khóa mới), mà còn gọn gàng khởi gợi ý 
cho các liên kết đó.
Stack với constructors và destructor
Stack with onstructors and destructor
//: C06:Stack3Test.cpp 
//{L} Stack3 
//{T} Stack3Test.cpp 
// Constructors/destructors
#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, rgv[1]); 
Stack textlines; 
tri g 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 texlines được popped xóa bỏ
Nhưng nếu chúng không được, bạn sẽ nhận được require( )
Thông báo rằng có sự rò rỉ bộ nhớ.
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
 C + +, Aggregate initialization làm cho khởi tạo an toàn
hơn nhiều. Khi bạn tạo một đối tượng mà là một tổng
hợp, tất cả các bạn phải làm là tạo một phân công, và
khởi tạo sẽ được chăm sóc bởi trình biên dịch.
Chuyển nhượng này có trong một số hương vị, 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 chuyển
nhượng phải được bao quanh bởi các dấu ngoặc nhọn. 
Đối với một mảng xây dựng trong các loại này khá đơn
giản:
Ví dụ:
int a[5] = { 1, 2, 3, 4, 5 };
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
Nếu bạn cố gắng cung cấp cho initializers
hơ đó 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 cho initializers ít hơn? 
Ví dụ:
int b[6] = {0}; 
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
Ở đây, trình biên dịch sẽ sử dụng initializer đầu tiên
cho các phần tử mảng đầu tiên, và sau đó sử dụ
số không cho tất cả các yếu tố mà không initializers. 
Thông báo khởi 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 initializers. 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 off-by-one trong những lỗi (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. ) A 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 initializers:
 int c[ ] = { 1, 2, 3, 4 };
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
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 trick trong một cách mà
không cần phải được thay đổ nếu kích thước
mảng changes5:
for(int i = 0; i < sizeof c / sizeof *c; i++) 
c[i]++; 
Nếu bạn có một mảng các đối tượng, bạn có thể khởi
tạo 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'} }; 
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
* Các hàm khởi tạo phải được gọi là thực hiện. Vì vậy, nếu
bạ 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à một trong những rõ ràng như sau:
Y y1[] = { Y(1), Y(2), Y(3) };
TỔNG QUAN VỀ KHỞI TẠO
Aggregate initialization
Dưới đây là một ví dụ thứ hai đối số hiển thị nhiều hàm dựng: 
#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 hàm dựng được gọi là rõ ràng cho
từng đối tượng trong mảng đó.
CÁC CONSTRUCTOR MẶC ĐỊNH
Default c structors
- Một construct r mặc định là một hàm không có đối số.
- Một constructor mặc định được sử dụng để tạo ra một “vanilla 
object”, nhưng nó cũng rất quan trọng khi nói với trình biên dịch là
để tạo ra một đối tượng nhưng không đưa ra bất kỳ chi tiết. 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 mặc đị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 mặc định. 
CÁC CONSTRUCTOR MẶC ĐỊNH
Default constructors
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ẽ khiếu nại vì nó phải có một constructor 
mặc định để khởi tạo mọi đối tượng trong mảng vấn đề này
cùng xảy ra. nếu bạn tạo một đối tượng cá nhân như thế
này: 
Y y4;
 nếu bạn có một hàm dựng, trình biên dịch bảo đảm cho xây
dựng luôn luôn xảy ra, bất kể tình hình. Các constructor 
mặc định là quan trọng như vậy là nếu (và chỉ nếu) không có
constructor cho một cấu trúc (struct hay class), trình biên
dịch sẽ tự động tạo ra cho bạn
CÁC CONSTRUCTOR MẶC ĐỊNH
Default constructors
/ / Tự động ạo ra constructor mặc định
class V { 
int i; // private 
}; // No constructor 
int main() { 
V v, v2[10]; 
} ///:~ 
 Nên xác định constructor một cách đàng hoàn, 
không nên để trình biên dịch làm việc đó thay bạn.
TỔNG KẾT
Summary
 costructor và destru tors cho phép khởi tạo
phù hợp và đảm bảo sạch (trình biên dịch sẽ không
cho phép một đối tượng được tạo ra và bị phá hủy
mà không có hàm dựng phù hợp và các cuộc gọi
destructor), bạn sẽ có toàn quyền kiểm soát và an 
toàn khởi tạo tổng hợp thu được bao gồm trong một
tĩnh mạch tương tự. -- nó ngăn bạn làm cho những
sai lầm khởi điển hình với các uẩn của built-in các
loại và làm cho mã của bạn thêm gọn gàng an toàn
trong quá trình mã hóa. Là một vấn đề lớn trong C + 
+. Khởi tạo và dọn dẹp là một phần quan trọng.
Exercises
1. Viết một lớp đơn giản gọi là đơn giản với một
constructor à in cái gì để cho bạn biết rằng nó
được gọi là. Trong main () làm cho một đối tượng
của class của bạn. 
2. Thêm một destructor để Exercise 1 mà in ra một
thông báo cho bạn biết rằng nó được gọi là. 
3. Bài tập 2 sửa đổi để các lớp có chứa một thành viên
int. Sửa đổi các constructor để nó phải mất một
đối số int rằng các cửa hàng trong thành viên lớp. 
Cả hai constructor và destructor nên in ra các giá
trị int là một phần của thông điệp của họ, vì vậy
bạn có thể nhìn hấy các đối ượng khi chúng được
tạo ra và tiêu huỷ.
4. Chứng minh rằng destructors vẫn còn gọi là ngay
cả khi goto được sử dụng để nhảy ra khỏi một
vòng. 
The End
Thực hiện bởi:
Lâm T anh Cường 08520526
Nguyễn Chí Công 08520525

File đính kèm:

  • pdfTrình bày chương 6 trong cuốn Thinking in C++ Volum 1.pdf
  • rarCStack.rar