1. Trang chủ
  2. » Công Nghệ Thông Tin

Mảng con trỏ và tham chiếu

35 4,3K 5
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Mảng, con trỏ và tham chiếu
Thể loại Tài liệu
Định dạng
Số trang 35
Dung lượng 61,88 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Bài giảng lập trình hướng đối tượng - Thầy Cường Học viện bưu chính viễn thông TP HCM

Trang 1

Chương 3

Mảng , Con trỏ và Tham chiếu

• Mảng các đối tượng

• Con trỏ đối tượng

• Con trỏ this

• Toán tử new và delete

• Truyền tham chiếu cho đối tượng

• Trả về các tham chiếu

• Các tham chiếu độc lập và các hạn chế

Trang 3

I/ Mảng các đối tượng

Các đối tượng chính là các biến , có các khả năng và thuộc tính như các biến thông thường khác Do đó, các đối tượng có thể tổ chức thành mảng

Cú pháp khai báo một mảng các đối tượng hoàn toàn giống như ngôn ngữ C

Việc truy cập mảng các đối tượng cũng giống như mảng của các loại biến khác

Ví dụ 1.1 Mảng các đối tượng

for(i=0; i<4; i++) ob[i].set_a(i); // truy cập mảng đối tượng

for(i=0; i<4; i++) cout << ob[i].get_a( );

Trang 4

int get_a() { return a; }

@ Một cách viết khác (dài hơn)

samp ob[4] = { samp(-1), samp(- 2), samp(-3), samp(- 4) };

@ Cách khởi đầu ở trên chỉ làm việc với các mảng có hàm tạo chỉ nhận một đối số

• Khởi đầu mảng đối tượng nhiều chiều

Trang 5

for(i=0; i<4; i++) {

Khi khởi đầu một mảng đối tượng có hàm tạo nhận nhiều đối số , cần phải dùng

dạng khởi đầu khác

int get_a() { return a; }

int get_b() { return b; }

for(i=0; i<4; i++) {

Trang 6

squares(int a, int b) { num = a; sqr = b; }

void show() {cout << num << ' ' << sqr << "\n"; }

};

II/ Con trỏ đối tượng

Các đối tượng có thể được truy cập thông qua con trỏ, toán tử -> sẽ được dùng

Khai báo một con trỏ đối tượng giống như khai báo một con trỏ hướng về kiểu biến bất kỳ Ví dụ samp *p;

Để có điạ chỉ của một đối tượng, dùng toán tử & đặt trước đối tượng Ví dụ

Trang 7

myclass ob(120); // create object

myclass *p; // create pointer to object

p = &ob; // put address of ob into p

cout << "Value using object: " << ob.get() << "\n" ;

cout << "Value using pointer: " << p->get();

return 0;

}

@ Việc tạo ra một con trỏ đối tượng không tạo ra một đối tượng, nó chỉ tạo ra một

con trỏ trỏ về đối tượng

Trang 8

• Số học con trỏ :

+ Khi tăng con trỏ đối tượng, nó sẽ trỏ đến đối tượng tiếp theo

+ Khi giảm con trỏ đối tượng, nó sẽ trỏ đến đối tượng đứng trước

int get_a() { return a; }

int get_b() { return b; }

p = ob ; // get starting address of array

for(i=0; i<4; i++) {

Trang 9

Bài tập II

1 Hãy viết lại ví dụ 2.2 chương 3 để cho nó hiển thị nội dung của mảng ob theo thứ tự ngược lại

2 Hãy viết lại ví dụ 1.3 chương 3 để truy cập mảng hai chiều qua con trỏ

III/ Con trỏ this

this là con trỏ được truyền tự động cho bất kỳ hàm thành viên nào khi được gọi và nó là con trỏ tới đối tượng tạo ra lời gọi hàm

Ví dụ, cho câu lệnh ob.f1() ; // ob là đối tượng

Hàm f1() tự động được truyền con trỏ ob là đối tượng tạo ra lời gọi hàm Con trỏ này

được xem là this

Chỉ có các hàm thành viên được truyền con trỏ this Hàm friend không có con trỏ

this

Con trỏ this có nhiều sử dụng, kể cả việc giúp quá tải các toán tử

• Khi một hàm thành viên tham chiếu một hàm thành viên khác của lớp, nó thực

hiện mà không xác định tham chiếu với hoặc một lớp hoặc một đặc tả đối tượng

Trang 10

Khi một hàm thành viên được gọi, nó tự động được truyền con trỏ this trỏ về đối

tượng tạo ra lời gọi Chương trình có thể viết lại :

Trang 11

{

strcpy(this->item, i); // access members

this->cost = c; // through the this

Bài tập III

Hãy chuyển tất cả các tham chiếu thích hợp đối với các thành viên của lớp thành tham chiếu con trỏ this

Trang 12

IV/ Toán tử new và delete

1/ Toán tử new dùng để cấp phát bộ nhớ động và toán tử delete dùng giải phóng bộ

nhớ đã cấp phát

Cú pháp p_var = new data_type;

delete p_var;

data_type chỉ định kiểu đối tượng muốn cấp phát bộ nhớ

p_var con trỏ tới kiểu đó

Giống như hàm malloc(), nếu không đủ bộ nhớ theo yêu cầu cấp phát thì toán tử new sẽ trả về con trỏ NULL Toán tử delete được gọi chỉ với một con trỏ đã được cấp phát trước đó qua toán tử new Nếu gọi delete với một con trỏ không hợp lệ, hệ thống cấp phát sẽ bị hủy, và có thể làm hỏng chương trình

Các ưu điểm

+ toán tử new tự động cấp phát bộ nhớ để giữ một đối tượng có kiểu được chỉ rõ + toán tử new tự động trả về một con trỏ có kiểu được chỉ rõ

+ toán tử new và delete có thể được quá tải

+ có thể khởi đầu đối tượng được cấp phát động

Trang 13

+ không cần nạp thư viện malloc.h hoặc stdlib.h vào trong chương trình

• Cấp phát bộ nhớ động để giữa một số nguyên

cout << "Here is integer at p: " << *p << "\n";

delete p; // release memory

Trang 14

int get_product() { return i*j; }

Bài tập IVa

1 Hãy viết chương trình sử dụng new để cấp phát động một float, một long và một char Cho các biến động này những giá trị và hiển thị Sau đó, hãy dùng delete giải phóng tất cả bộ nhớ

2 Hãy tạo một lớp có chứa tên và số điện thoại của một người Dùng new để cấp phát động một đối tượng của lớp này, lưu tên và số điện thoại vào đối tượng đó rồi hiển thị

2/ Các đặc điểm của new và delete

+ các đối tượng được cấp phát động một giá trị đầu

p_var = new data_type(initial_value) ;

Trang 15

+ các mảng được cấp phát động có thể được tạo ra

p_var = new data_type[size] ;

p_var trỏ tới đầu mảng có phần tử size có kiểu được chỉ rõ

Vì lý do kỹ thuật, không thể khởi đầu một mảng đã được cấp phát động

+ để hủy bỏ mảng đã được cấp phát động

delete [] p_var ; p_var sẽ được giải phóng một lần

• cấp phát bộ nhớ cho một số nguyên và khởi đầu bộ nhớ đó

cout << "Here is integer at p: " << *p << "\n";

delete p; // release memory

return 0;

}

• truyền các giá trị đầu cho một đối tượng được cấp phát động

Trang 16

samp(int a, int b) { i=a; j=b; }

int get_product() { return i*j; }

Trang 17

p = new int [5]; // allocate room for 5 integers

// always make sure that allocation succeeded

for(i=0; i<5; i++) p[i] = i;

for(i=0; i<5; i++) {

cout << "Here is integer at p[" << i << "]: ";

void set_ij(int a, int b) { i=a; j=b; }

int get_product() { return i*j; }

};

int main()

Trang 18

for(i=0; i<10; i++) p[i].set_ij(i, i);

for(i=0; i<10; i++) {

cout << "Product [" << i << "] is: ";

void set_ij(int a, int b) { i=a; j=b; }

~samp() { cout << "Destroying \n"; }

int get_product() { return i*j; }

};

int main()

Trang 19

for(i=0; i<10; i++) {

cout << "Product [" << i << "] is: ";

@ Kết quả của chương trình có gì khác so với ví dụ 4.6 ?

Bài tập IVb

1 Hãy chuyển đổi đoạn chương trình sau để dùng toán tử new

char *p;

p = (char *) malloc(100);

//

strcpy(p, "This is a test");

2/ Sử dụng toán tử new, cấp phát một double và cho gaí trị đầu là -3.1416

V/ Tham chiếu (reference)

Trang 20

1/ Một tham chiếu là một con trỏ ẩn tác động như một tên khác đối với một biến

Khai báo : data_type &var

Cách sử dụng :

+ Một tham chiếu có thể được truyền cho hàm (gọi là tham số tham chiếu)

+ Một tham chiếu có thể được trả về bởi hàm

+ Một tham chiếu độc lập có thể được tạo ra

• Hoạt động của tham số tham chiếu (reference parameter)

Chương trình này chứng tỏ cách con trỏ được dùng như một tham số để tạo ra một cơ

chế truyền tham số gọi bằng tham chiếu

• Quá trình trên có thể thực hiện tự động bằng cách dùng tham số tham chiếu

Trang 21

// f() now uses a reference parameter

void f(int &n)

{

// notice that no * is needed in the following statement

n = 100; // put 100 into the argument used to call f()

}

n là một biến tham chiếu

f() được khai báo như một tham số tham chiếu, điạ chỉ đối với số được truyền tự động cho f()

2/ Đặc tính

• Khi sử dụng tham số tham chiếu , trình biên dịch sẽ tự động truyền địa chỉ của biến được dùng như đối số Không cần tạo ra điạ chỉ của đối số bằng cách đặt trước nó ký tự &

• Bên trong hàm, trình biên dịch tự động dùng biến được trỏ tới bởi tham số tham chiếu Không cầân dùng ký tự * Do đó, một tham số tham chiếu hoàn toàn thực hiện tự động cơ chế truyền đối số bằng cách gọi tham chiếu

• Không thể thay đổi những gì mà một tham chiếu trỏ tới

3/ Ưu điểm của tham số tham chiếu

Trang 22

+ Từ quan điểm thực hành, không cần nhớ truyền điạ chỉ của một đối số

+ Tham số tham chiếu tạo ra một giao diện đẹp hơn so với cách dùng cơ chế con trỏ + Khi một đối tượng được truyền cho một hàm như một tham chiếu thì không có bản sao được thực hiện

• Dùng các tham chiếu để trao đổi 2 đối số nguyên

Nếu dùng con trỏ thì hàm swapargs() được viết lại như sau :

void swapargs(int *x, int *y)

Trang 24

// decompose num into whole and fractional parts

frac = modf(num, &val);

if(frac < 0.5) num = val;

else num = val+1.0;

}

Bài tập V

1 Hãy viết hàm neg() để đảo ngược dấu của các tham số nguyên Theo hai cách :

+ dùng tham số con trỏ

+ dùng tham số tham chiếu

2 Tìm lỗi sai trong chương trình sau :

// This program has an error

// Triple num's value

void triple(double &num)

Trang 25

Trong chương 2, khi một đối tượng được truyền cho một hàm bằng cách dùng cơ chế truyền tham số gọi giá trị, một bản sao đối tượng được tạo ra Khi hàm trả về, hàm hủy của bản sao đối tượng được gọi, điều này có thể sinh ra những vấn đề nghiêm trọng, chẳng hạn hàm hủy giải phóng bộ nhớ động

Khi truyền đối số bằng tham chiếu, không có bản sao đối tượng được tạo ra, do đó hàm hủy của nó không được gọi khi các hàm trả về

Tuy nhiên, những thay đổi đối với đối tượng ở bên trong hàm có ảnh hưởng đến đối tượng được dùng như đối số

Khi một đối tượng được truyền bởi tham chiếu thì toán tử truy cập thành viên vẫn là

toán tử điểm (.) chứ không phải toán tử mũi tên (->)

• Truyền đối tượng bằng giá trị cho hàm được gọi f()

~myclass() { cout << "Destructing " << who << "\n"; }

int id() { return who; }

Trang 26

Giải thích kết quả ?

• Truyền đối tượng bằng tham chiếu cho hàm được gọi f()

~myclass() { cout << "Destructing " << who << "\n"; }

int id() { return who; }

};

// Now, o is passed by reference

void f(myclass &o)

{

// note that operator is still used!!!

cout << "Received " << o.id() << "\n";

Trang 29

Một hàm có thể trả về một tham chiếu Điều này có ích khi quá tải một số loại toán tử nào đó

Một hàm cũng có thể được sử dụng ở bên trái của câu lệnh gán

• Hàm trả về một tham chiếu

Trang 30

Ví duï 7.2

// Return an int reference

int &f()

{

int x; // x is now a local variable

return x; // returns a reference to x

char &put(int i);

char get(int i);

Trang 32

Bài tập VII

1 Hãy viết chương trình tạo mảng an toàn hai chiều 2x3 các số nguyên

2 Đoạn chương trình sau có đúng không ? Tại sao ?

VIII/ Các tham chiếu độc lập và các hạn chế

1/ Tham chiếu độc lập (independent reference) là một biến tham chiếu có tác dụng chỉ là một tên khác cho một biến khác

Tham chiếu độc lập phải được khởi đầu khi khai báo, do các tham chiếu không thể được gán những giá trị mới

Các lập trình viên thường ít sử dụng tham chiếu độc lập

2/ Một số hạn chế

+ không thể tham chiếu đến một tham chiếu khác

+ không thể có điạ chỉ của tham chiếu

+ không thể tạo một mảng các tham chiếu

+ không thể tham chiếu một trường bit

+ các tham chiếu phải được khởi đầu trừ khi chúng là các thành viên của lớp, là các giá trị trả về hoặc là các tham số của hàm

• Chương trình có một tham chiếu độc lập

Trang 33

int &ref = x; // create an independent reference

x = 10; // these two statements

ref = 10; // are functionally equivalent

const int &ref = 10;

Bài tập chương 3

Trang 34

1 Cho lớp sau, hãy tạo một mảng hai chiều 3x4 và cho mỗi đối tượng trong mảng một giá trị đầu

Ngày đăng: 21/08/2012, 15:34

TỪ KHÓA LIÊN QUAN

w