Chủ Nhật, ngày 03 tháng 1 năm 2010

// // Leave a Comment

C++ Bài 5.4: Chuyển đổi kiểu nâng cao

Cho đến nay, để có thể chuyển đổi kiểu một đối tượng đơn giản sang kiểu khác chúng ta đã sử dụng toán tử chuyển đổi kiểu truyền thống. Ví dụ, để chuyển một số dấu phẩy động có kiểu double sang dạng số nguyên có kiểu int chúng ta sử dụng:

int i;
double d;
i = (int) d;

hoặc

i = int (d);

Điều này làm việc tốt đối với các kiểu cơ bản đã có định nghĩa các cacchs chuyển đổi cơ bản, tuy nhiên những toán tử này cũng có thể được áp dụng bừa bãi với các lớp và con trỏ tới các lớp. Bởi vậy, hoàn toàn hợp lệ khi viết như sau:
// class type-casting
#include

class CDummy {
int i;
};

class CAddition {
int x,y;
public:
CAddition (int a, int b) { x=a; y=b; }
int result() { return x+y;}
};

int main () {
CDummy d;
CAddition * padd;
padd = (CAddition*) &d;
cout <<>result();
return 0;
}

Mặc dù chương trình trên là hợp lệ trong C++ (thực tế là nó sẽ được dịch mà không có bất kì một lỗi hay warning nào đối với hầu hết các trình dịch) nhưng nó là một đoạn mã không tốt lắm vì chúng ta sử dụng hàm result, đó là một thành viên của CAddition, padd không phải là một đối tượng, nó chỉ là một con trỏ được chúng ta gán cho địa chỉ của một đối tượng không có quan hệ. Khi truy xuất đến thành viên result, chương trình sẽ tạo ra lỗi run-time hoặc chỉ là một kết quả không mong muốn.

Để có thể điều khiển việc chuyển đổi kiểu giữa các lớp, chuẩn ANSI-C++ đã định nghĩa bốn toán tử chuyển đổi kiểu mới: reinterpret_cast, static_cast, dynamic_castconst_cast. Tất cả chúng đều có cùng dạng thức khi sử dụng:

reinterpret_cast <new_type> (expression)
dynamic_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
Trong đó new_type kiểu mà expression phải được chuyển đổi thành. Để tạo ra sự tương tự dễ hiểu với các toán tử chuyển đổi truyền thống các biểu thức này có nghĩa là:

(new_type) expression
new_type (expression)

reinterpret_cast

reinterpret_cast chuyển đổi một con trỏ sang bất kì kiểu con trỏ nào khác. Nó cũng cho phép chuyển đổi từ con trỏ sang dạng số nguyên và ngược lại.

Toán tử này có thể chuyển đổi con trỏ giữa các lớp không có quan hệ với nhau. Kết quả của toán tử này là một bản copy giá trị của con trỏ này sang con trỏ kia. Dữ liệu được trỏ đến không hề được kiểm tra hay chuyển đổi.

Trong trường hợp chuyển đổi giữa con trỏ và số nguyên, cách chuyển nội dung của nó phụ thuộc vào hệ thống.

class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast(a);

reinterpret_cast đối xử với tất cả các con trỏ giống như các toán tử chuyển đổi truyền thống.

static_cast

static_cast cho phép thực hiện bất kì phép chuyển đổi nào

static_cast allows to perform any casting that can be implicitly performed as well as also the inverse cast (even if this is not allowed implicitly).

Applied to pointers to classes, that is to say that it allows to cast a pointer of a derived class to its base class (this is a valid conversion that can be implicitly performed) and can also perform the inverse: cast a base class to its derivated class.

In this last case the base class that is being casted is not checked to determine wether this is a complete class of the destination type or not.

class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast(a);

static_cast, aside from manipulating pointers to classes, can also be used to perform conversions explicitly defined in classes, as well as to perform standard conversions between fundamental types:

double d=3.14159265;
int i = static_cast(d);

dynamic_cast

dynamic_cast is exclusively used with pointers and references to objects. It allows any type-casting that can be implicitly performed as well as the inverse one when used with polymorphic classes, however, unlike static_cast, dynamic_cast checks, in this last case, if the operation is valid. That is to say, it checks if the casting is going to return a valid complete object of the requested type.

Checking is performed during run-time execution. If the pointer being casted is not a pointer to a valid complete object of the requested type, the value returned is a NULL pointer.

class Base { virtual dummy(){}; };
class Derived : public Base { };

Base* b1 = new Derived;
Base* b2 = new Base;
Derived* d1 = dynamic_cast(b1); // succeeds
Derived* d2 = dynamic_cast(b2); // fails: returns NULL

If the type-casting is performed to a reference type and this casting is not possible an exception of type bad_cast is thrown:

class Base { virtual dummy(){}; };
class Derived : public Base { };

Base* b1 = new Derived;
Base* b2 = new Base;
Derived d1 = dynamic_cast(b1); // succeeds
Derived d2 = dynamic_cast(b2); // fails: exception thrown

const_cast

This type of casting manipulates the const attribute of the passed object, either to be set or removed:

class C {};
const C * a = new C;
C * b = const_cast (a);

Neither of the other three new cast operators can modify the constness of an object.

typeid

ANSI-C++ also defines a new operator called typeid that allows to check the type of an expression:

typeid (expression)

this operator returns a refernece to a constant object of type type_info that is defined in standard header file . This returned value can be compared with another using operators == and != or can serve to obtain a string of characters representing the data type or class name by using its name() method.
// typeid, typeinfo
#include
#include

class CDummy { };

int main () {
CDummy* a,b;
if (typeid(a) != typeid(b))
{
cout << "a and b are of different types:\n";
cout << "a is: " << typeid(a).name() << '\n';
cout << "b is: " << typeid(b).name() << '\n';
}
return 0;
}
a and b are of different types:
a is: class CDummy *
b is: class CDummy

0 comments: