[Basic] Namespace và ép kiểu trong C++

Namespace

Như ta đã biết không thể định nghĩa nhiều định danh cùng tên trong cùng 1 phạm vi truy cập vì trình biên dịch sẽ không xác định được sử dụng định danh nào, khi đó compiler hoặc linker sẽ thông báo lỗi

Ví dụ:

File A.h

int doSomeThing(int a, int b)
{
   return a + b;
}

File B.h

int doSomeThing(int a, int b)
{
   return a - b;
}

File Main.cpp

#include <iostream>
#include "A.h"
#include "B.h"

int main()
{
   int val = doSomeThing(3,4);
   
   return 0;
}

Kết quả:

error C2084: function 'int doSomeThing(int,int)' already has a body

Ở ví dụ trên ta định nghĩa hàm doSomeThing trong 2 file A.h và B.h sau đó include vào Main.cpp, khi đó trong file Main.cpp tồn tại 2 hàm doSomeThing, khi biên dịch trình biên dịch sẽ sẽ thông báo.

Để giải quyết vần đề này ta có thể làm:

C1 : Đổi tên 1 trong 2 hàm doSomeThing.

=> Không nên sử dụng cách này vì mỗi tên ta đặt ra thường thể hiện chức năng và mục đích của hảm, biến. Khi ta đổi tên khác đi có thể ý nghĩa thể hiện không được rõ ràng nữa.

C2 : Ta sử dụng namespace

Namespace sẽ định nghĩa 1 phạm vi mà ở đó tất cả các định danh là duy nhất.

Mặc định các biến global và các hàm global được địn nghĩa trong global namespace.

Với ví dụ trên ta có thể định nghĩa các hàm doSomeThing trong các namespace khác nhau.

File A.h

namespace A
{
   int doSomeThing(int a, int b)
   {
      return a + b;
   }
}

File B.h

namespace B
{
   int doSomeThing(int a, int b)
   {
      return a - b;
   }
}

File Main.cpp

#include <iostream>
#include "A.h"
#include "B.h"

int main()
{
   //Truy cập hàm doSomeThing của namespace A
   int val = A::doSomeThing(3,4);
   
   return 0;
}

Kết quả:

val = 7;

Chú ý:

  • Để truy cập các thành phần trong namespace ta sử dụng toán tử ‘::’
tên_namespace :: Định_danh
  • Ta có thể sử dụng 1 namespace trên nhiều file khác nhau, khi đó các thành phần trong các file đó cùng namespace.

Ví dụ

File A.h

namespace Test
{
   int Add(int a, int b)
   {
      return a + b;
   }
}

File B.h

namespace Test
{
   int Sub(int a, int b)
   {
      return a - b;
   }
}

=> Theo ví dụ trên hàm Sub và Add cùng namespace Test.

Câu lệnh using

Trong các chương trình C++ ta thường thấy dòng lệnh rất quen thuộc như

using namespace std;

Ta thử tìm hiểu về câu lệnh trên.

  • std là 1 namespace trong thư viên chuẩn của C++
  • using để nói với trình biên dich ta sử dụng các thành phần bên trong namespace của std.

Các sử dụng using:

  • Ta có thể dùng using để chỉ định thành phần mà ta muốn sử dụng

Ví dụ :

#include <iostream>

int main()
{
   using std::cout;
   
   cout << "Hello world!";
   
   return 0;
}

Ở ví dụ ta sử dụng câu lệnh “using std::cout;” để nói với trình biên dịch rằng ta sử dụng cout trong namespace std, sau đó để sử dụng ta chỉ cần “cout << “Hello world! ” ;

Với cách sử dụng này chỉ đích danh 1 thành phần, với các thành phần khác ta vẫn phải dùng toán tử ‘::

  • Một cách khác cho phép chúng ta sử dụng mọi thứ trong namespace
using namespace tên_namespace;
#include <iostream>

int main()
{
   using namespace std;
   
   cout << "Hello world!";
   
   return 0;
}

=> Với cách dùng này ta có thể sử dụng trực tiếp mọi thành phần trong namespace std mà không cần thông qua toán tử ::

Ép kiểu trong C++

Ép kiểu là chuyển đổi 1 giá trị từ kiểu dữ liệu này sang dữ liệu khác.

Trong C++ có 2 loại ép kiểu cơ bản:

  • Ép kiểu ngầm định (implicit type conversion).
  • Ép kiểu tường minh (explicit type conversions)

Ép kiểu ngầm định

Thường được sử dụng trên các kiểu dữ liệu cơ bản.

Ép kiểu ngầm định sẽ được thực hiện tự động bởi trình biên dịch, chuyển đổi kiểu dự liệu cơ bản này sang kiểu dữ liệu khác.

Ví dụ:

float val = 3;

Ta có số 3 là literal có kiểu int.

=> Trình biên dich sẽ tự động chuyển đổi sang kiểu float và gán cho biến val.

Chú ý:

  • Việc ép kiểu ngầm định đôi khi cho kết quả không như mong muốn.

Ví dụ:

float res = 10 / 4;

=> vì 10 và 4 đều là số nguyên

=> kết quả phép chia sẽ được ép ngầm định sang số nguyên là 2. Sau đó 2 được chuyển đổi ngầm định sang kiểu số thực 2.0 để gán cho res

=> res = 2; Kết quả không như mong muốn res = 2.5

=> Để khắc phục lỗi này ta dùng ép kiểu tường minh.

Ép kiểu tường minh

Ép kiểu tường mình sẽ được thực hiện bởi các lập trình viên. Các lập trình viên sẽ sử dụng các cách ép kiểu mà C++ hỗ trợ để chuyển đổi sang kiểu dữ liệu mình mong muốn.

Trong C++ hỗ trợ 5 cách ép kiểu khác nhau:

  • C-style casts
  • static casts
  • const casts
  • dynamic casts
  • reinterpret_cast

C-style casts

Éo kiểu giống trong ngôn ngữ C, sử dụng toán tử ()

Cú pháp : (new_type) expression hoặc new_type (expression)

Ví dụ:

int i1 = 10;

int i2 = 4;

float f = (float) i1 / i2;

Chú ý :

  • Không nên sử dụng C-style casts

Vì sử dụng C-style casts trình biên dịch sẽ không kiểm tra tính hợp lệ trong quá trình biên dịch nên có thể xảy ra lỗi trong lúc runtime.

static_cast

Cú pháp : static_cast < new_type > ( expression )

Ví dụ:

int i1 = 10;

int i2 = 4;

float f = static_cast<float> (i1) / i2;

Chú ý :

  • Nên sử dụng static_cast

Vì sử dụng static_cast trình biên dịch sẽ kiểm tra tính hợp lệ trong quá trình biên dich, nên khó xảy ra lỗi trong quá trình runtime, nên sử dụng static_cast sẽ an toàn hơn C-style casts.

const_cast

Cú pháp : const_cast < new_type > ( expression )

const_cast dùng để loại bỏ const của biến con trỏ.

Ví dụ

int val = 3;

const int* ptr = &val;

*const_cast<int*> (ptr) = 10;

=> Ta dùng const_cast để loại bỏ const của biến ptr, khi đó ta có thể thay đổi giá trị của nó.

Chú ý :

  • Không nên sử dụng const_cast.
  • new_type trong const_cast phải là kiểu con trỏ (pơinter) hoặc tham chiếu (Reference)

reinterpret_cast

Cú pháp : reinterpret_cast < new_type > ( expression )

reinterpret_cast giống với C-style casts. Không nên sử dụng.

Typedef và Auto

Typedef

Typedef cho phép ta tạo 1 tên gọi khác cho 1 kiểu dữ liệu và sử dụng tên đó thay cho kiểu dữ liệu thực.

Ví dụ:

typedef double distance_t; // Định nghĩa distance_t như 1 tên gọi khác của double

//Hai câu lệnh sau sẽ tương đương nhau
double val;

distance_t val;

Chú ý:

  • Ta nên đặt tên của typedef với hậu tố là “_t”
typedef char int8_t

typedef int int16_t
  • Typedef không phải là định nghĩa 1 kiểu dữ liệu mới mà chỉ tạo 1 tên gọi khác cho 1 kiểu dữ liệu đã tồn tại.

Auto

Một biến được khai báo là auto để nói cho trình biên dịch rằng biến đó có kiểu dữ liệu giống với kiểu dữ liệu của giá trị mà nó được khởi tạo.

Ví dụ:

auto val = 5.0; //Biến val có kiểu double

int age = 20;

auto myAge = age; //Biến myAge có kiểu int

Chú ý:

  • Phải luôn khởi tạo giá trị cho biến khai báo Auto
auto val = 10; //OK

auto age; // ERROR

age = 20;
  • Không sử dụng biến auto cho parameter của hàm
//ERROR
int Add (auto a, auto b)
{
   return a + b;
}
  • Sử dụng auto cho kiểu trả về của hàm

C++ 14:

auto Add (int a, int b);

C++ 11:

auto Add (int a, int b) -> int;

Was this article helpful?

Có 2 bình luận

  1. Hello. I see that you don’t update your website too often. I know
    that writing articles is boring and time consuming.
    But did you know that there is a tool that allows you to create new
    articles using existing content (from article directories or other blogs from your niche)?
    And it does it very well. The new articles are
    unique and pass the copyscape test. You should try miftolo’s tools

  2. I think this is among the most vital info for me.

    And i am glad reading your article. But want to remark on some general things, The website style is great, the articles is really nice :
    D. Good job, cheers

Leave A Comment?