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
•
Tham chiếu (reference)
•
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ế
Chöông 3
Maûng, Con troû, Tham chieáu
68
68
Chương 3
Mảng, Con trỏ, Tham chiếu
69
69
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
#include <iostream.h>
class samp {
int a;
public:
void set_a(int n) { a = n; }
int get_a() { return a; }
};
int main()
{
samp ob[4]
; // khai báo mảng đối tượng
int i;
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( );
return 0;
}
•
Nếu kiểu lớp
có hàm tạo, thì mảng đối tượng có thể được khởi đầu
.
Ví dụ 1.2
// Initialize an array.
#include <iostream.h>
class samp {
int a;
public:
samp(int n) { a = n; }
Chương 3
Mảng, Con trỏ, Tham chiếu
70
70
int get_a() { return a; }
};
int main()
{
samp
ob[4] = { -1, -2, -3, -4 }
;
int i;
for(i=0; i<4; i++) cout << ob[i].get_a() << ' ';
return 0;
}
@ 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.
Ví dụ 1.3
// Create a two-dimensional array of objects.
#include <iostream.h>
class samp {
int a;
public:
samp(int n) { a = n; }
int get_a() { return a; }
};
int main()
{
samp
ob[4][2] = { 1, 2,
3, 4,
5, 6,
7, 8 };
int i;
Chương 3
Mảng, Con trỏ, Tham chiếu
71
71
for(i=0; i<4; i++) {
cout <<
ob[i][0].
get_a() << ' ';
cout <<
ob[i][1].
get_a() << "\n";
}
return 0;
}
•
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.
Vídụ 1.4
#include <iostream.h>
class samp {
int a, b;
public:
samp(int n, int m)
{ a = n; b = m; }
int get_a() { return a; }
int get_b() { return b; }
};
int main()
{
samp
ob[4][2] = { samp(1, 2), samp(3, 4),
samp(5, 6), samp(7, 8),
samp(9, 10), samp(11, 12),
samp(13, 14), samp(15, 16) }
;
int i;
for(i=0; i<4; i++) {
cout <<
ob[i][0].
get_a() << ' ';
cout <<
ob[i][0].
get_b() << "\n";
cout <<
ob[i][1].
get_a() << ' ';
cout <<
ob[i][1].
get_b() << "\n";
}
return 0;
}
Chương 3
Mảng, Con trỏ, Tham chiếu
72
72
Bài tập I
1. Dùng khai báo lớp sau đây để tạo mảng 10 phần tử và khởi đầu phần tử ch với các
giá trò từ A đến J. Hãy chứng tỏ mảng chứa các giá trò này.
#include <iostream.h>
class letters {
char ch;
public:
letters(char c) { ch = c; }
char get_ch() { return ch; }
};
2. Dùng khai báo lớp sau đây để tạo mảng 10 phần tử và khởi đầu num với các giá
trò từ 1 đến 10 và hãy khởi đầu spr đối với bình phương của num.
#include <iostream.h>
class squares {
int num, sqr;
public:
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ụ
p = &ob;
Chương 3
Mảng, Con trỏ, Tham chiếu
73
73
Ví dụ 2.1
#include <iostream.h>
class myclass {
int a;
public:
myclass(int x); // constructor
int get();
};
myclass::myclass(int x)
{
a = x;
}
int myclass::get()
{
return a;
}
int main()
{
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.
Chương 3
Mảng, Con trỏ, Tham chiếu
74
74
•
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.
Ví dụ 2.2
// Pointers to objects.
#include <iostream.h>
class samp {
int a, b;
public:
samp(int n, int m)
{ a = n; b = m; }
int get_a() { return a; }
int get_b() { return b; }
};
int main()
{
samp ob[4] = {
samp(1, 2),
samp(3, 4),
samp(5, 6),
samp(7, 8)
};
int i;
samp *p;
p = ob
; // get starting address of array
for(i=0; i<4; i++) {
cout << p->get_a() << ' ';
cout << p->get_b() << "\n";
p++
; // advance to next object
}
return 0;
}
Chương 3
Mảng, Con trỏ, Tham chiếu
75
75
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
.
Ví dụ 3.1
// Demonstrate the
this
pointer.
#include <iostream.h>
#include <string.h>
class inventory {
char item[20];
double cost;
int on_hand;
public:
inventory(char *i, double c, int o)
Chương 3
Mảng, Con trỏ, Tham chiếu
76
76
{
//
tham chiếu trực tiếp các biến item[], cost, on_hand
strcpy(item, i);
cost = c;
on_hand = o;
}
void show();
};
void inventory::show()
{
cout << item;
cout << ": $" << cost;
cout << " On hand: " << on_hand << "\n";
}
int main()
{
inventory ob("wrench", 4.95, 4);
ob.show();
return 0;
}
•
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 :
Ví dụ 3.2
#include <iostream.h>
#include <string.h>
class inventory { // Demonstrate the
this
pointer.
char item[20];
double cost;
int on_hand;
public:
inventory(char *i, double c, int o)
Chương 3
Mảng, Con trỏ, Tham chiếu
77
77
{
strcpy(
this->item
, i); // access members
this->cost
= c; // through the this
this->on_hand
= o; // pointer
}
void show();
};
void inventory::show()
{
cout << this->item; // use this to access members
cout << ": $" << this->cost;
cout << " On hand: " << this->on_hand << "\n";
}
int main()
{
inventory ob("wrench", 4.95, 4);
ob.show();
return 0;
}
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.
#include <iostream.h>
class myclass {
int a, b;
public:
myclass(int n, int m) { a = n; b = m; }
int add() { return a+b; }
void show();
};
void myclass::show()
Chương 3
Mảng, Con trỏ, Tham chiếu
78
78
{
int t;
t = add(); // call member function
cout << t << "\n";
}
int main()
{
myclass ob(10, 14);
ob.show();
return 0;
}
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.
Chương 3
Mảng, Con trỏ, Tham chiếu
79
79
+ 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
Ví dụ 4.1
// A simple example of new and delete.
#include <iostream.h>
int main()
{
int *p;
p = new int
; // allocate room for an integer
if(!p) {
cout << "Allocation error\n";
return 1;
}
*p = 1000;
cout << "Here is integer at p: " << *p << "\n";
delete p
; // release memory
return 0;
}
•
Cấp phát động một đối tượng
Ví dụ 4.2
// Allocating dynamic objects.
#include <iostream.h>
class samp {
int i, j;
public:
void set_ij(int a, int b) { i=a; j=b; }
Chương 3
Mảng, Con trỏ, Tham chiếu
80
80
int get_product() { return i*j; }
};
int main()
{
samp *p;
p = new samp
; // allocate object
if(!p) {
cout << "Allocation error\n";
return 1;
}
p->set_ij(4, 5);
cout << "Product is: " << p->get_product() << "\n";
delete p
; // release memory
return 0;
}
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
)
;