Mar 30, 2009

Effective C++ [ phan 2 ] - Làm quen hẹn hò

Chương 1. Làm quen với ngôn ngữ C++
Mục 1: Nhìn C++ như một liên ngôn ngữ
Từ bắt đầu C++ chỉ là C với một vài những đặc điểm hướng đối tượng. Thậm chí C++ còn được gọi là “C với những lớp”, mối liên hệ này chỉ là một sự kế thừa.
Là một ngôn ngữ sung mãn
Ngày nay C++ là một ngôn ngữ lập trình đa mô thức “multiparadigm programming language” vì thế để nắm bắt C++ bạn cần phải hiểu bạn đang làm việc với phần nào của C++ thật may nắm là nhưng phần này là 4 cây cột lớn trong C++. Tuỳ thuộc việc bạn đang làm gì thì bạn nên hiểu được tác dụng của nó. Trong C++ có 4 cây cột lớn chống đỡ. Đó là:

C. lý do chính là C++ là được phát triển từ C trong rất nhiều các trường hợp C++ tiếp cận vấn đề theo C. Nhưng bạn cũng nên nhớ những giới hạn của C đó là không template, không overload, không

Object Orriented C++: Hướng đối tượng C++ đây là một phần của C++ nó chính là cái được gọi là C với những lớp. Nó bao gồm đa hình, kế thừa,… Đây là phạm vi mà những khái niệm OOP có tác dụng

Template C++ : Mẫu trong C++ đây là phần generic programming một phần của C++. Mục 46 sẽ đề cầp đến nó Trên thực tế mẫu là một mô thức lập trình rất mạnh mẽ.

STL đây là bộ thư viện chuẩn của C++ sử dụng mẫu. trong này cung cấp rất nhiều những tiện ích cho lập trình của bạn.

Chú ý:
Bạn cần phải biết là bạn đang làm việc với phần nào trong 4 cây cột lớn của C++

Mục 2: consts, enums và inlines thay cho #defines
Mục này tốt nhất nên được gọi là chú ý vào việc định nghĩa tiền sử lý cho bộ biên dịch. Hãy chú ý khi sử dụng #define ví dụ

#define ASPECT_RATIO 1.653

Cái tên ASPECT_RATIO sẽ không bao giờ được trình biên dịch biết đến vì bộ tiền sử lý đã loại bỏ nó đi. Nó có thể gây ra lỗi khi trình biên dịch triệu gọi sử dụng rất có thể lỗi sẽ tham chiếu đến 1.653 chứ không phải là ASPECT_RATIO và thật không may nếu ASPECT_RATIO được viết trong một file header mà bạn không biết đến thì bạn chẳng thể biết được 1.653 ở đâu ra cả !
Giải pháp là bạn sẽ thay thế macro này với một hằng như sau

const double AspectRatio = 1.653; // uppercase names are usually for
// macros, hence the name change
Như là một hằng ngôn ngữ AspectRatio nó là một định nghĩa và được nhìn nhận bởi trình biên dịch. Một điểm lợi hại của const là bạn chỉ tạo ra một phiên bản duy nhất của AspectRatio chứ không giống như định nghĩa bằng #define mỗi lần nó sẽ thay thế giá trị của AspectRatio bằng một phiên bản của 1.653 hẳn nhiên code của bạn sẽ nhiều lên trông thấy.

Một vấn đề khác nữa là con trỏ kiểu hằng. tốt nhất bạn nên khai báo const 2 lần ví dụ
const char * const authorName = "Scott Meyers";
Để có một thảo luận đầy đủ với việc sử dụng const, đặc biệt là cách phù phép với con trỏ xem mục 3 tuy nhiên bạn sử dụng đối tượng string vì thế cách dưới đây là cách định nghĩa tốt hơn.

const std::string authorName("Scott Meyers");

Dưới đây là một trường hợp đặc biệt thứ 2 của lớp hằng. Giới hạn phạm vi hằng của một lớp bạn cần làm nó như một biến thành viên, và chắc chắn rằng chỉ có một bản sao duy nhất của hằng bạn đặt thêm từ khoá static để chắc về điều đó

class GamePlayer {
private:
static const int NumTurns = 5; // constant declaration
int scores[NumTurns]; // use of constant
...
};

Cái bạn nhìn thấy ở trên là một khai báo cho NumTurns nó không phải là một định nghĩa. Thông thường C++ yêu cầu một định nghĩa cho bất cứ cái thứ gì mà bạn sử dụng. Nhưng lớp hằng đặc biệt tĩnh và có kiểu int hoặc bool, hoặc char .. là những ngoại lệ. Miễn là bạn không cần tới địa chỉ của chúng thì bạn có thể khai báo chúng, sử dụng chúng mà không cần định nghĩa.
Để sử dụng hằng double
class CostEstimate {
private:
static const double FudgeFactor; // declaration of static class
... // constant; goes in header file
};

const double // definition of static class
CostEstimate::FudgeFactor = 1.35; // constant; goes in impl. file

Trong những trường hợp không muốn đao to búa lớn như static bạn có thể sử dụng enum để thay thế hoàn toàn có tác dụng tương tự nhưng sẽ không tốn kém như static
class GamePlayer {
private:
enum { NumTurns = 5 }; // "the enum hack" — makes
// NumTurns a symbolic name for 5
int scores[NumTurns]; // fine
...
};

Khi sử dụng
// call f with the maximum of a and b
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

Thì nên thay thế bằng
template<typename T >
inline void callWithMax(const T& a, const T& b) // know what T is, we
{ // pass by reference-to-
f(a > b ? a : b); // const — see Item 20
}

Chú ý :
Sự đơn giản của hằng. Nên dùng const hoặc enum thay vì #define
Chức năng giống macro thì nên sử dụng inline thay cho #define


Mục 3: Sử dụng consts bất cứ khi nào có thể !
Những điều tuyệt vời về const là cho phép bạn sử dụng một ngữ nghĩa đặc biêt là đối tượng không được sửa đổi và trình biên dịch sẽ đảm bảo điều này. Nó cho phép bạn kết nối 2 trình biên dịch khác nhau của những lập trình viên khác và giá trị cũng không thay đổi. Điều này luôn đúng.

Từ khoá const đặc biệt xuất sắc, bên ngoài lớp nó định nghĩa một phạm vi toàn cục hoặc phạm vi namespace. Cũng tương tự như một đối tượng khai báo static ở một file, một khối phạm vi, bên trong lớp. bạn có thể sử dụng cả static không static. Cho con trỏ, bản thân nó là const dữ liệu của con trỏ là cost cả hai hoặc tuỳ.

char *p = greeting; // non-const pointer,
// non-const data

const char *p = greeting; // non-const pointer,
// const data

char * const p = greeting; // const pointer,
// non-const data

const char * const p = greeting; // const pointer,
// const data

Cú pháp không bất thường như bạn nhìn thấy. Nếu từ const xuất hiện phía bên trái của dấu hoa thị (*) thì cái được chỏ tới là hằng (vùng nhớ con trỏ trỏ tới là hằng). Nếu từ const xuất hiện bên phải của đấu hoa thị (*) thì bản thân con trỏ là hằng, nếu xuất hiện cả 2 bên thì cả 2 đều là hằng. Tóm lại theo sau cost cái gì thì cái đó là hằng

Khi nào cái được chỏ tới là hằng một vài lập trình viên liệt kê cost trước kiểu, một số khác liệt kê nó trước kiểu nhưng sau dâu hoa thị (*) nói chung nó không có gì khác biệt. Ví dụ dưới đây mô tả các cách khác nhau của cùng một dạng tham số:

void f1(const Widget *pw); // f1 takes a pointer to a
// constant Widget object
void f2(Widget const *pw); // so does f2

thường thì cả 2 dạng này đều xuất hiện trong code nên bạn cũng nên quen với chúng.

STL iterator được mô hình dựa trên con trỏ vì thế cho nên một iterator hành động giống như một con tro T* cũng vì thế nên khi khai báo một const iterator cũng giống như khai báo một hằng con trỏ (ví như cost T*). iterator không cho phép chỏ tới một cái gì đó khác nhưng cái mà nó chỏ tới có thể được thay đổi. Nếu bạn muốn một iterator chỏ tới một cái gì đó cũng không thay đổi bạn muốn const_iterator

std::vector vec;
...
const std::vector::iterator iter = // iter acts like a T* const
vec.begin();

*iter = 10; // OK, changes what iter points to
++iter; // error! iter is const

std::vector::const_iterator cIter = //cIter acts like a const T*
vec.begin();

*cIter = 10; // error! *cIter is const
++cIter; // fine, changes cIter

Một vài sử dụng mạnh mẽ nhất của const phụ thuộc vào ứng dụng khai báo chức năng, trong một khai báo chức năng const có thể tham chiếu tới giá trị chả về, tới tham số riêng và những chức năng thành phần cho cái lệ thuộc. Một chức năng chả về kiểu hàng có thể làm giảm đi những tác động nguy hiểm từ phía client ví dụ

class Rational { ... };
const Rational operator*(const Rational& lhs, const Rational& rhs);

Rất nhiều lập trình viên có cái nhìn lệch lạc về nó khi lần đầu tiên nhìn thấy khai báo. Tại sao kết quả của một operator* lại là một đối tượng const, nếu không phải là như thế thì client se có thể tác động thế này

Rational a, b, c;
...
(a * b) = c; // invoke operator= on the
// result of a*b!

Tôi không hiểu sao rất nhiều lập trình viên muốn sử dung phép gán để làm thủ tục có 2 số, nhưng tôi biết rất nhiều người đã làm thế này mà không lo lắng đây là một sự ép kiểu mặc định (một kiểu có thể converted ngầm định thành kiểu bool)

if (a * b = c) ... // oops, meant to do a comparison!

Với một kiểu người dùng định nghĩa tốt, sẽ tránh được những sự không tương thích một cách vô lý. Khai báo toán tử operator* trả về một giá trị là const là một điều nên làm

Không có những gì đặc biệt mới về hằng tham số (const parameter) chúng hành sự như những đối tượng const cục bộ. và bạn có thể sử dụng cả 2 bất cứ khi nào bạn có thể. Chừ phi bạn cần phải cho phép một đối tượng hoặc một tham số cục bộ có thể bị sửa đổi. hãy chắc rằng bạn đã khai báo nó là const bạn chit mất thêm có 6 ký tự cho công việc này và nó có thể cứu bạn tránh khỏi những rắc rối khó chụi kiểu như

Hằng chức năng const
Mục đích của const trên chức năng thành phần là để nhận ra những chức năng nào có thể bị chiệu gọi trên một hằng đối tượng. có 2 lý do quan trọng để làm việc này, Đầu tiên là sự đơn giản trong việc hiểu giao diện của một lớp điều này khẳng định những chức năng nào có thể thay đổi đối tượng và những cái nào thì không. Thứ 2 chúng có thể làm việc với những đối tượng hằng. Đó là một bình phẩm về diện mạo của cách viết code hiệu xuất. vì lý do đó nó sẽ đc diễn giải trong trong mục 20 một trong những cách thức nhằm cải thiện hiệu quả cho việc các chương trình viết bằng C++ đó là việc chuyền đối tượng tham chiếu hằng. Kỹ thuật này chỉ có tác dụng nếu ở đó có hàm thành phần const. Cái sẽ thao tác những kết quả hằng đối tượng.
Có rất nhiều người không nhận thấy việc sử dụng const trong hàm thành viên cũng có thể tạo ra hiệu ứng overloaded và đây là một đặc điểm quan trọng của C++

class TextBlock {
public:
...
const char& operator[](std::size_t position) const // operator[] for
{ return text[position]; } // const objects

char& operator[](std::size_t position) // operator[] for
{ return text[position]; } // non-const objects

private:
std::string text;
};

Nhờ việc sử dụng như thế TextBlock có thể dc sử dụng như sau:

TextBlock tb("Hello");
std::cout << pc =" &cctb[0];" pc =" 'J';" textlength =" std::strlen(pText);" lengthisvalid =" true;" textlength =" std::strlen(pText);" lengthisvalid =" true;">( // cast away const on
// op[]'s return type;
static_cast(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
...
};

Như các bạn thấy là hàm non-const của chúng ta sẽ gọi đến hàm const. Bạn không thể trực tiếp gọi đến const bởi vì cái bạn đang sử dụng là phiên bản non-const (đối tượng this) và như thế bạn không thể gọi đến một hàm const của một đối tượng non-const đây là lý do mà bạn phải chuyển kiểu như bạn đã thấy

Điều ghi nhớ
• Sử dụng const giúp cho trình biên dịch dễ dò tìm lỗi, const được sử dụng với một phạm vi áp dụng rộng rãi từ kiểu trả về, hàm, đối tượng…
• Tăng hiệu quả của trình biên dịch, nhưng trong trương trình của bạn buộc phải sử dụng thuộc tính hằng
• Khi tạo ra 2 phiên bản chức năng hằng và không hằng, những code trùng lặp được loại bỏ bằng viêc hàm không hằng gọi tới hàm hằng.

Mục 4: Chắc chắn rằng đối tượng được cài đặt trước khi sử dụng
Phần nảy trong nguyên bản có một vài điều những thiết nghĩ không quan trọng lắm nên không dịch bạn có thể tham khảo ở bản gốc của tác giả.

Trong C++ khi bạn khai báo một đối tựơng không hẳn nó đã được tạo ra. Và dữ liệu nó có thể dùng được, đảm bảo chắc nguyên tác bạn đang tạo ra cái gì, bạn sử dụng cái gì tốt nhất nên khởi gán giá trị của các biên, con trỏ khi bạn dùng.

Bạn thực hiện đúng điểu này sẽ giúp bản kiểm soát một cách chính sác mọi qua trình trong lớp, trong trương trình của bạn.


No comments:

Post a Comment

 
Bạn có thể dùng bài viết của tôi tùy ý bạn nhưng vui lòng ghi lại rõ nguồn cung cấp
The world in a click_
Copyright © 2008 linhdkl