OPENLAB-IMAGE PROCESSING
Bạn có muốn phản ứng với tin nhắn này? Vui lòng đăng ký diễn đàn trong một vài cú nhấp chuột hoặc đăng nhập để tiếp tục.

Các tiêu chuẩn của ngôn ngữ lập trình C++

Go down

Các tiêu chuẩn của ngôn ngữ lập trình C++ Empty Các tiêu chuẩn của ngôn ngữ lập trình C++

Bài gửi by jackauk Wed May 18, 2016 12:24 am

Hai tổ chức tiêu chuẩn quốc tế (the International Standards Organization (ISO) và the International Electrotechnical Commission (IEC) )đã thống nhất ra các bộ tiêu chuẩn cho ngôn ngữ lập trình C++

Cụ thể:
Year C++ Standard Informal name
1998 ISO/IEC 14882:1998[14] C++98
2003 ISO/IEC 14882:2003[15] C++03
2011 ISO/IEC 14882:2011[6] C++11
2014 ISO/IEC 14882:2014[16] C++14
2017 to be determined        C++17

NHững điều quan trọng của C++01



Được sửa bởi jackauk ngày Wed May 18, 2016 9:50 pm; sửa lần 4.
jackauk
jackauk
Thành viên thường

Tổng số bài gửi : 63
Điểm danh tiếng : 2
Join date : 16/08/2015
Age : 35
Đến từ : TP Hồ Chí Minh

Về Đầu Trang Go down

Các tiêu chuẩn của ngôn ngữ lập trình C++ Empty Re: Các tiêu chuẩn của ngôn ngữ lập trình C++

Bài gửi by jackauk Wed May 18, 2016 4:46 pm

Sự khác biệt quan trọng giữa C++98 và C++03 với C++11

Chúng ta không bàn về sự khác nhau giữa C++98 với C++03 bởi giữa chúng có rất ít khác biệt. Khi phiên bả C++11 ra đời nó như là bản làm mới C++ bởi những khác biệt của nó với 2 bản tiêu chuẩn trước. Các trình biên dịch hiện nay đều đã cập nhật C++11.
Nguồn đoạn sau từ PCwork
http://www.pcworld.com.vn/articles/cong-nghe/cong-nghe/2014/03/1234591/chuan-c-11-nhu-mot-ngon-ngu-lap-trinh-moi/

Là một trong những ngôn ngữ lập trình phổ biến nhất trên thế giới (cùng với C, Objective-C, và Java [1]), C++ được Stroustrup tạo ra vào đầu những năm 1980. Đến năm 1998 C++ đã trở thành một chuẩn (standard) được công nhận, chuẩn này thường được gọi là C++98, qui ước đặt tên chuẩn gắn liền với năm cũng bắt đầu từ đây.
Năm 2005, ủy ban tiêu chuẩn của ISO (với các thành viên là Stroustrup và các lập trình viên C++ hàng đầu trên thế giới [2]) đã thông qua chuẩn C++0x và tới năm 2011, chuẩn C++11 đã chính thức được ban hành với khá nhiều thay đổi quan trọng. Chuẩn tiếp theo của C++ dự kiến sẽ được thông qua vào năm 2014 và sẽ có tên là C++14. Trong bài viết này, nhằm mang lại cho các bạn độc giả một cái nhìn tổng quan, tôi sẽ trình bày về các khía cạnh nổi bật nhất, cùng với một số sách, phần mềm cần thiết để có thể học, hiểu và lập trình theo chuẩn C++11.

Đầu tiên cần phải hiểu rõ mục đích của việc xây dựng các chuẩn mới (C++11, C++14) vì bản thân C++98 (với các khái niệm vẫn đang được phổ biến trong các khóa dạy về lập trình C++, lập trình hướng đối tượng trong các trường đại học và vẫn được các lập trình viên C++ dùng để viết các phần mềm) hoặc C++0x đã ổn định và khá hiệu quả, vậy tại sao lại cần thêm các chuẩn này? Có phải chỉ để đưa vào các khái niệm mới, trừu tượng hơn và chỉ phục vụ cho việc phát triển các thư viện (như STL) hay không? Mục đích của việc đưa ra các chuẩn C++ mới, thể hiện rõ nhất với C++11 (vì C++0x không có nhiều thay đổi lớn so với C++98) là nhằm:

+ Đạt được hiệu năng cao hơn (tốc độ, sử dụng bộ nhớ, tận dụng các cấu trúc phần cứng mới-các bộ xử lý đa nhân ...) với các chương trình viết bằng C++.

+ Hiệu quả lập trình cao hơn qua việc cung cấp các lớp, cú pháp và kỹ thuật lập trình mới nhằm đẩy mạnh xu hướng lập trình generic.
+ Có sự tương thích với các phiên bản C++ cũ (C++98, C++0x).
+ Không đưa thêm vào quá nhiều các khái niệm mới hoặc các tính năng chưa ổn định.
+ Có thể xem chi tiết hơn ở [3].

Trên thực tế có nhiều thứ được đưa vào chuẩn C++11 là đã có và đang được sử dụng rộng rãi như smart pointers, lambda, thread, chrono ... đa số trong số này nằm trong boost [4], một tập các thư viện C++ rất hữu dụng, vì thế những ai đã quen với boost sẽ cảm thấy rất thuận tiện với chuẩn C++11.

Vậy làm thế nào có thể học lập trình C++ theo chuẩn C++11? Đầu tiên là tài liệu, hiện nay có các tài liệu sau:

1. The C++ language, 4th edition, 2013, quyển sách này được viết bởi chính Stroustrup, rất đầy đủ và cập nhật nhưng chủ yếu có tác dụng tra cứu chứ không giúp ích nhiều cho việc triển khai công việc thực tế.

2. Professional C++ 2nd Edition, 2011, một cuốn sách cập nhật (so với C++11) và được đánh giá khá cao.

3. Data Structures and Algorithm Analysis in C++, 4th edition, 2014, một cuốn sách rất mới và hay, viết về cấu trúc dữ liệu và giải thuật với C++ của giáo sư Mark Allen Weiss, Florida International University, cập nhật chuẩn C++11.

4. The C++ Standard Library, 2nd edition, 2012. Một cuốn chuyên lập trình C++ theo chuẩn C++11 với phần lớn nội dung dành cho thư viện STL. Các bạn đang học cấu trúc dữ liệu hoặc STL có thể đọc quyển này và cuốn số 3

5. C++ Concurrency in Action, 2012, của Anthony Williams, cho tới hiện nay, vẫn là cuốn duy nhất viết về lập trình đa luồng (multithread) đầy đủ nhất với C++11. Cuốn này dành riêng cho chủ đề đa luồng, tương tranh.

Tiếp đến là phần mềm, cần phân biệt hai loại, compiler và editor. Về compiler phổ biến nhất là G++ (GNU C++) thuộc GCC, MS VC (Microsoft Visual C++ compiler) thuộc bộ phần mềm phát triển tích hợp Visual Studio của MS. Ngoài ra còn có bộ biên dịch Clang cho Mac OS, bộ compiler của Intel ... Trong khi G++ (từ bản 4.8.1 được phát hành tháng 5-2013) đã cài đặt đầy đủ các tính năng mới của chuẩn C++11 thì MS VC (dù là bản 2013) vẫn còn nhiều tính năng chưa được hỗ trợ (chẳng hạn như constexpr hay list-initialization, có thể là do các developper của MS cho rằng các tính năng đó chưa quan trọng lắm...). Tiếp đến là editor, nếu chọn bộ VS thì không cần lo vấn đề này vì VS nổi tiếng về sự tiện lợi (tất nhiên vẫn có những chỗ hơi bất tiện, nhưng là số ít), ngược lại có thể dùng Orwell DevCpp (được phát triển tiếp từ DevCpp của hãng BloodShed), rất thích hợp cho việc dùng với gcc và môi trường giáo dục, dành cho những ai mới bắt đầu hoặc đơn giản là cấu hình máy tính không thích hợp cho việc cài VS và các phần mềm khác, vì bộ này (cùng với GCC) không cần cài đặt và yêu cầu cấu hình thấp, dung lượng nhỏ. Code::Block là một IDE có nhiều tính năng tiện lợi hơn DevCpp và cũng miễn phí, tiếp đến là Qt Creator, cũng miễn phí và dễ dùng. Mạnh nhất (trong số các phần mềm miễn phí, và cũng khó dùng hơn) là Eclipse. Vì bộ compiler của MS VS không hỗ trợ đầy đủ nên theo tôi nếu có thể thì nên dùng bản VS 2012 Update 4 (đối với các hệ thống cũ 32 bit, chip Core 2 Duo, 4GB Ram) hoặc VS 2013 Update 1 cho cập nhật.

Sau đây là các đặc điểm nổi bật nhất của chuẩn C++11.
1. Khởi tạo theo kiểu danh sách (List initialization)
Tính năng này cho phép chúng ta có thể khai báo và khởi tạo các giá trị ban đầu cho một đối tượng thuộc một lớp chứa nào đó một cách linh hoạt theo cú pháp sau:

Code:
<class type> object = {arg1, arg2, …, argn};
Ví dụ:

vector<int> vi = {2, 8, 0, 3};
vector<string> vs = {“abc”, “xyz”};
Thực chất thì tính năng này C++ học tập từ C (khai báo và khởi tạo mảng) và bắt đầu đưa vào từ chuẩn C++0x, đến C++11 thì hoàn thiện với việc đưa vào lớp bản mẫu std::initializer_list<T> được gọi tới một cách tự động bởi trình biên dịch qua cú pháp {}.

2. Các hàm lambda (còn gọi là lambda expressions)
Xuất phát từ các lớp toán tử hàm (Functor classes), là các lớp cài đặt toán tử gọi hàm operator(), thường được dùng như các vị từ (predicate) cho các thuật toán của thư viện bản mẫu chuẩn STL, lambda được đưa vào chuẩn C++11 để giảm bớt công việc của các lập trình viên mà vẫn đạt được hiệu năng cao. Điều này xuất phát từ thực tế là mỗi khi cần sử dụng một vị từ, một lập trình viên cần phải viết code cho cả lớp toán tử hàm đó (cấu tử, khai báo các thành viên dữ liệu, và toán tử gọi hàm, tất nhiên) nên thường họ có xu hướng viết hẳn một vòng lặp thay vì gọi một vị từ, kết quả là việc tối ưu mã nguồn của trình biên dịch không được thực hiện, đồng nghĩa với hiệu năng chương trình không được cải thiện. Lambda đóng vai trò như một hàm không tên (anonymous/unnamed function) và trình biên dịch sẽ thay thế chúng bằng một con trỏ hàm khi dịch chương trình. Hãy xem ví dụ sau khi chúng ta cần gọi hàm sort() để sắp xếp một mảng số thực lưu trong một vector:

Code:
// kiểu Functor class
class Compare{
public:
bool operator()(const float a, const float b){
return a<b;
}
};
// gọi hàm
vector<float> vf;
// … gán giá trị cho vf;
std::sort(&vf[0], &vf[0] + vf.size(), Compare());
// kiểu mới theo chuẩn C++11 với lambda function
std::sort(&vf[0], &vf[0] + vf.size(), [](float a, float b) {
return a<b;
});
Về hiệu năng không có sự khác biệt vì cả hai hàm so sánh đều là hàm inline, nhưng rõ ràng việc sử dụng lambda cho một cú pháp linh hoạt và đơn giản hơn. Có một điều mà không phải lập trình viên C/C++ nào cũng biết là hàm qsort() nổi tiếng của C cũng hoạt động theo cách tương tự với một hàm so sánh được định nghĩa như toán tử của lớp Compare ở trên, cú pháp gọi hàm cũng tương tự nhưng tốc độ lại chậm hơn 2 lần so với hàm sort() của C++.

Cú pháp khai báo một hàm lambda được thực hiện như sau:

Code:
[<danh sách các biến ngoài và kiểu>](<danh sách các tham số>)-> <kiểu trả về của hàm>{
// câu lệnh
}
Trong đó <danh sách các biến ngoài> cho phép một hàm lambda có thể sử dụng các biến ngoài theo cách mà một hàm bình thường tác động lên các biến nằm trong phạm vi hoạt động của nó, còn <danh sách các tham số> và <kiểu trả về> thì hoàn toàn giống như các hàm C/C++ khác. Cú pháp -> <kiểu trả về của hàm> cũng là một cú pháp mới được bổ sung vào C++11 và được gọi là trailing return type. Cú pháp này cho phép khai báo kiểu trả về của một hàm sau prototype của một hàm, một kỹ thuật rất quan trọng khi dùng với các hàm, lớp bản mẫu (xem chi tiết hơn ở 8.1).

Lambda đặc biệt hữu ích khi cần viết các vị từ và Boost (Boost.Lambda và Boost.Phoenix) đã sử dụng khái niệm lambda từ lâu nhưng cho tới mãi chuẩn C++11 (tức là sau 7 năm từ khi STL ra đời) nó mới được đưa thành một chuẩn. Các lập trình viên Python, C# thậm chí đã quen thuộc với khái niệm này từ trước đó.

3. Rvalue reference và move constructor, move assignment

Khái niệm lvalue và rvalue không có gì xa lại đối với các lập trình viên C++, thường trong một câu lệnh gán:
Code:

<đối tượng> = <biểu thức>;
Thì <đối tượng> được gọi là lvalue (left value) còn <biểu thức> được gọi là rvalue (right value) của biểu thức. Một cách logic thì đối tượng lvalue sẽ có giá trị thay đổi, còn đối tượng rvalue sẽ không thay đổi giá trị. Xét chi tiết hơn trong lệnh gán trên, trình biên dịch sẽ thực hiện tính giá trị biểu thức nằm bên trái của phép gán, sau đó gán giá trị tính được vào địa chỉ của giá trị lvalue bên phải của phép gán. Như vậy về bản chất phép gán trên có kết quả lưu vào tham chiếu (qua tên) tới một đối tượng. Tổng quát hơn, bất cứ một biểu thức nào (như lệnh gán, lời gọi hàm …) cần lưu kết quả vào một tham chiếu, nó sẽ là một lvalue. Ngược lại bất cứ biểu thức nào trả về giá trị dưới dạng một đối tượng thì đó chính là một rvalue. Vấn đề của các rvalue là chúng có thời gian tồn tại rất ngắn trong chương trình, do chỉ dùng cho các biến tạm, trung gian nên cần phải gán chúng cho các lvalue để có thể dùng lại sau này.

Khái niệm rvalue reference ra đời giúp kéo dài tuổi thọ của các đối tượng rvalue và hơn thế nữa, nó giúp cho việc lập trình hiệu quả hơn rất nhiều. Chúng ta hãy xem ví dụ đơn giản sau:

Code:
vector<VerybigType> v1;
for(int i=0;i<n;++i)
{
VerybigType bigObj;
….
v1.push_back(bigObj);
}
Khi đoạn mã lệnh trên được thực hiện, mỗi lần lặp môt bản sao của bigObj sẽ được tạo ra, và sau đó được dùng để tạo ra đối tượng v1[i] qua hàm copy constructor. Tiếp đến đối tượng bản sao cũng sẽ bị hủy. Có thể thấy rằng việc truyền đối tượng bigObj như vậy sẽ là một thao tác tốn thời gian (cấp phát bộ nhớ, copy dữ liệu) vì kích thước của nó có thể rất lớn. Rvalue reference giúp chúng ta giải quyết vấn đề này như sau:

Code:
vector<VerybigType> v1;
for(int i=0;i<n;++i)
{
VerybigType bigObj;
….
v1.push_back(std::move(bigObj));
}
Ở đây, C++11 thực hiện một kỹ thuật gọi là move semantics để tối ưu code chương trình với các đối tượng rvalue reference dựa trên nguyên lý làm giảm số thao tác cấp phát (allocate) và copy dữ liệu của các đối tượng có kích thước lớn. Trong ví dụ trên khi thực hiện câu lệnh v1.push_back(std::move(bigObj)), thay vì tạo ra một bản sao của bigObj và thực hiện hàm copy constructor của lớp VerybigType, trình biên dịch sẽ thực hiện chuyển (move, không phải copy) toàn bộ nội dung của bigObj cho đối tượng v1[i], với std::move(bigObj) là một rvalue reference, điều này giúp tránh được việc loại bỏ đối tượng bigObj khỏi bộ nhớ, cũng như việc thực hiện copy dữ liệu (qua phép việc gọi hàm copy constructor) từ bigObj sang v1[i]. Rõ ràng là một công đôi việc.

Để thực hiện kỹ thuật move semantics, các lớp cần cài đặt hai hàm: move constructor và move assignment operator với prototype như sau:

Code:
<class name>::<class name>(<class name> && rhs); // C++11 move constructor
<class name> & <class name>::operator=(<class name> && rhs); // C++11 move assignment operator
Trong đó “&&” là cú pháp khai báo một rvalue reference. Dù có sự khác nhau về cách thức thực hiện và ý nghĩa, nhưng hai hàm này đều có điểm chung là tham số đầu vào là một rvalue reference và đối tượng được tham chiếu đến sẽ bị loại bỏ sau khi dữ liệu của nó được chuyển (move) cho đối tượng nhận giá trị trả về của hàm. Hầu hết các lớp chứa của thư viện STL C++11 (trừ lớp std::array) đều cài đặt hai hàm trên.

4. Delete and default functions
Một lớp C++11 sẽ có 6 loại hàm thành viên đặc biệt dùng để tạo, hủy, khởi tạo, chuyển đổi kiểu và sao chép các đối tượng:
+ Các hàm tạo mặc định (Default constructors)
+ Các hàm hủy (Destructors)
+ Các hàm tạo copy (Copy constructors)
+ Các hàm toán tử gán copy (Copy assignment operators)
+ Các hàm tạo move (Move constructors)
+ Các hàm toán tử gán move (Move assignment operators)
Một cách tự nhiên, các trình biên dịch có nhiệm vụ tự sinh các hàm này khi chúng được gọi đến mà không được lập trình viên xây dựng cho lớp của mình và thường thì nhiệm vụ này được trình biên dịch thực hiện tốt. Nhưng có hai trường hợp nảy sinh: thứ nhất là có một số hàm mà bạn không muốn trình biên dịch tự sinh vì bạn không muốn hàm đó, thứ hai là khi bạn có một hàm tạo (chẳng hạn như hàm tạo có tham số nhưng không không phải mặc định) thì trình biên dịch sẽ không tự động sinh các hàm tạo mặc định nữa nhưng bạn vẫn muốn có hàm đó. Để giải quyết hai tình huống này, C++11 đưa ra hai từ khóa chỉ định cho các hàm là default và delete, hãy xem ví dụ sau:

Code:
class X{
public:
X(int v){
// cài đặt
}
X() = default; // bạn đã có hàm tạo có tham số nhưng vẫn muốn hàm tạo mặc định
X(unsigned int) = delete; // không muốn một hàm tạo làm việc với kiểu unsigned int
X & operator=(const X& r) = delete; // không có hàm gán copy cho lớp này
};
Ngoài ra, một mục đích khác của hai từ khóa này là cho phép các hàm đặc biệt trên có thể là các hàm ảo, protected (không phải public).

5. Smart pointers

Smart pointers (tạm dịch là các con trỏ thông minh) là các đối tượng có cách làm việc giống như các con trỏ thông thường trong C/C++ (gọi là built-in/raw pointers, với các toán tử tham chiếu lại *, ->) nhưng có thêm một chức năng khác là quản lý các đối tượng được tạo ra bằng toán tử new: các đối tượng này sẽ được tự động xóa bỏ theo một cách thích hợp nhất và các lập trình viên sẽ không cần phải quan tâm tới việc quyết định xem cần loại bỏ chúng khi nào và ở đâu trong chương trình của mình. Các con trỏ thông minh thực hiện việc quản lý các đối tượng được cấp phát động dựa trên cơ chế quyền sở hữu (ownership) đối tượng: đoạn mã nào của chương trình sẽ thực hiện loại bỏ đối tượng khỏi bộ nhớ chương trình. Nếu không có các smart pointers, chúng ta chỉ có một cách duy nhất để loại bỏ các đối tượng cấp phát động bằng toán tử delete. Nhưng không phải lúc nào chúng ta cũng thực hiện được điều này một cách tường minh: một lệnh gán con trỏ sai sẽ làm mất địa chỉ của biến đã cấp phát, hoặc chương trình gặp lỗi không mong muốn trước khi có thể thực hiện lệnh delete, cả hai khả năng đều dẫn tới lỗi memory-leak. Với các smart pointers, công việc dọn dẹp này sẽ được thực hiện một cách tự động.

C++11 đưa ra 3 loại smart pointers (file header memory): shared_ptr, unique_ptr và weak_ptr. Shared_ptr thực hiện cơ chế chia sẻ quyền sở hữu tới các đối tượng mà nó trỏ tới: tất cả các con trỏ kiểu này đều có thể sở hữu một đối tượng được tạo ra và đối tượng sẽ bị loại bỏ khỏi bộ nhớ khi con trỏ cuối cùng bị loại bỏ. Weak_ptr thường kết hợp với share_ptr vì con trỏ kiểu này không sở hữu đối tượng, nó đơn giản được sử dụng để trỏ tới đối tượng (còn gọi là tài nguyên của một smart pointer) mà một share_ptr đang nắm giữ để kiểm tra xem đối tượng đó còn tồn tại hay không. Cách duy nhất để tạo ra một weak_ptr là tạo ra các share_ptr và gán cho nó. Kiểu con trỏ unique_ptr chỉ cho phép một con trỏ duy nhất có quyền sỡ hữu đối tượng tại một thời điểm, khi đối tượng có quyền sở hữu này bị loại bỏ, đối tượng mà nó sở hữu cũng sẽ bị loại bỏ.

6. Các hàm final và override
C++11 đưa ra hai từ khóa mới để kiểm soát kế thừa là final và override. Một lớp final sẽ không thể kế thừa, một hàm final sẽ không thể override. Một hàm của một lớp được khai báo với từ khóa override sẽ yêu cầu lớp cơ sở mà nó kế thừa có một hàm virtual với cùng prototype. Nhãn final sẽ giúp trình biên dịch tối ưu hóa lời gọi tới các hàm virtual tương ứng mà không cần thêm các bảng con trỏ hàm ảo để quản lý các hàm này. Còn nhãn override giúp các lập trình viên tránh khỏi các lỗi không đáng có khi một hàm mới hoàn toàn có thể được tạo ra một cách không mong muốn trong lớp kế thừa thay vì override một hàm đã có trong lớp cơ sở.

7. Lập trình song song (parallel programming) và xử lý tương tranh (concurrency management) với các luồng.
Có thể nói việc đưa vào các lớp thread, mutex, atomic, unique_lock, future là điểm nổi bật và đáng giá nhất của chuẩn C++11. Cũng như các đặc điểm đã được trình bày ở trên, đa số các khái niệm này đã có từ khá lâu và đã được sử dụng phổ biến trên thực tế (qua các thư viện như pthread, Windows thread, hay boost::thread) vì chúng cho phép tận dụng được các kiến trúc phần cứng multicores để tăng tốc độ của các chương trình qua cơ chế lập trình song song. Trong khi mục đích của lập trình song song là tăng tốc độ bằng việc chạy nhiều luồng (thread) trên các CPU core khác nhau cùng một lúc và là một lựa chọn không bắt buộc thì xử lý tương tranh là một yêu cầu có tính bắt buộc: trong đa số các chương trình có giao diện đồ họa (hoặc tương tác người dùng theo thời gian thực) và thao tác xử lý nền mất nhiều thời gian, bạn cần ít nhất 2 luồng, một để tương tác với người dùng qua giao diện (foreground thread), luồng còn lại sẽ xử lý công việc chính ở phía nền (background/worker thread).
Là một trong những ngôn ngữ lập trình phổ biến nhất trên thế giới (cùng với C, Objective-C, và Java [1]), C++ được Stroustrup tạo ra vào đầu những năm 1980. Đến năm 1998 C++ đã trở thành một chuẩn (standard) được công nhận, chuẩn này thường được gọi là C++98, qui ước đặt tên chuẩn gắn liền với năm cũng bắt đầu từ đây.
Để tạo ra một thread, có thể sử dụng một hàm, một lambda expression, một hàm thành viên của một lớp theo các cú pháp sau:

Code:
#include <thread>
// đối với các hàm
std::thread th(<tên hàm>,<danh sách tham số>);
// sử dụng lambda expression
std::thread th(<khai báo một hàm lambda>);
// đối với các phương thức của lớp
std::thread th(&<tên lớp>::<tên phương thức>,&<đối tượng>, <danh sách tham số>);
trong trường hợp luồng th được khai báo trong một phương thức của lớp thì &<đối tượng> sẽ thay bằng *this. Trong trường hợp phương thức được gọi tới là một overloading function thì ta cần khai báo một con trỏ hàm trỏ tới đúng hàm cần gọi. Để thực hiện luồng, ta gọi tới hàm join() như sau:

Code:
th.join();
Nếu có nhiều luồng, ví dụ:
std::thread th1(<…>); // khai báo luồng 1
std::thread th2(<…>); // khai báo luồng 2
// thực hiện
th1.join();
th2.join();
thì các câu lệnh join() sẽ làm cho các luồng này được thực hiện một cách đồng thời trên các luồng khác nhau của hệ điều hành (cài đặt bên dưới của C++11 thực hiện điều này).

Để tránh hiện tượng data race (còn gọi là race condition) xảy ra khi nhiều luồng cùng truy cập vào một vung dữ liệu và thay đổi nó, các lớp atomic, mutex (cùng với các hàm lock, unlock) có thể được sử dụng để tạo nên các đoạn mã thread-safe (đảm bảo không bị data-race, tại mỗi thời điểm chỉ có một luồng được phép thay đổi vùng dữ liệu chia sẻ).

Lớp future và async cho phép thực hiện các thao tác bất đồng bộ trong chương trình qua các luồng.
Cũng cần lưu ý một điều là cho tới thời điểm này (tháng 2-2014) trình biên dịch gcc 4.8.1 vẫn chưa hỗ trợ std::thread trên môi trường Windows.

8. Một phiên bản STL hiệu quả hơn
Với nhiều cải tiến dựa trên các đặc điểm mới như move semantics, smart pointers, lambda expression thư viện STL của C++11 đã hiệu quả hơn rất nhiều. Phần này trình bày một số đặc điểm khác của chuẩn C++11 được dùng để tăng hiệu năng của việc sử dụng STL, với kết thúc là một ví dụ minh họa sử dụng lớp chứa vector.

8.1. Các biến auto và khai báo kiểu decltype

Trước khi có chuẩn C++11, từ khóa auto đã được sử dụng cho các biến sẽ tự động được giải phóng khỏi bộ nhớ chương trình khi không dùng đến nữa. Mục đích của từ khóa này đã thay đổi, một biến auto của C++11 sẽ được dùng để hướng dẫn cho trình biên dịch tự suy luận ra kiểu phù hợp cho giá trị được dùng để khởi tạo biến đó vào lúc dịch chương trình, giúp tăng hiệu năng lập trình vì sẽ không phải gõ các kiểu có tên dài trong chương trình, ví dụ thay vì viết:
Code:

for(ten_lop_rat_dai it=v.begin();it!=v.end();++it)
ta sẽ viết:

for(auto it=v.begin();it!=v.end();++it)
Tuy nhiên có những trường hợp mà auto cũng không giải quyết được, ví dụ khi ta muốn xây dựng một hàm bản mẫu để nhân hai đối tượng như sau:

Code:
template<typename T1, typename T2>
void func1(T1 obj1, T2 obj2){
auto temp = obj1*obj2;
cout << temp;
}
Hàm func1 này hoạt động tốt nhưng sẽ thế nào nếu chúng ta muốn trả về giá trị là tích của obj1 và obj2 thay vì xuất ra cout? Khai báo kiểu của hàm func1 là T1 hay T2 đều không giúp được vì tích của obj1 và obj2 có thể thay đổi tùy vào kiểu của chúng: nếu T1 là kiểu số, T2 là kiểu ma trận thì kết quả trả về phải là một ma trận, hoặc nếu T1 và T2 là hai vector thì kết quả trả về phải là một số là tích vô hướng của chúng … có rất nhiều tình huống có thể xảy ra. C++11 giải quyết vấn đề này bằng cách đưa ra từ khóa decltype cho phép trình biên dịch có thể suy ra được chính xác kiểu trả về của hàm như sau:

Code:
template<typename T1, typename T2>
auto func2(T1 obj1, T2 obj2)->decltype(obj1*obj2){
return obj1*obj2;
}
Trong trường hợp này, auto không có nhằm mục đích hướng dẫn trình biên dịch tìm đúng kiểu cho hàm mà đơn giản là một phần của cú pháp trailing return type.

Như vậy, theo nghĩa mạnh hơn so với auto, decltype cho phép trình biên dịch suy ra được kiểu trả về của một biểu thức và hoàn toàn có thể dùng thay thế cho auto (trong các vòng lặp ví dụ ở trên) mặc dù điều này là không nên.

8.2. Các hàm non-member begin và end
Hầu như tất cả các lớp chứa của STL đều có các hàm thành viên là begin() và end() để trả về iterator cho phép truy cập tới các phần tử của lớp. Các hàm này cũng được sử dụng trong rất nhiều các thuật toán của STL, tuy nhiên các thuật toán này lại không thể áp dụng với các lớp chứa do người dùng tự xây dựng hoặc các mảng theo kiểu C. C++11 đưa ra hai hàm begin() và end() nhưng không phải là thành viên của bất cứ lớp chứa nào (còn gọi là các free functions). Các hàm này cũng trả về các iterator tới phần tử đầu tiên và cuối cùng của một lớp chứa, và có thể dễ dàng được overloading cho bất cứ lớp hay một mảng bất kỳ nào, thậm chí trong các trường hợp mà các lớp chứa là không thể thay đổi được. Điều này cho phép các lập trình viên viết các đoạn code có tính trừu tượng (generic) hơn. Vì vậy thay vì viết:

Code:
for(auto it=v.begin();it!=v.end();++it)
ta sẽ viết:
Code:
for(auto it=begin(v);it!=end(v);++it)
8.3. Vòng lặp cho các khoảng giá trị (Range-based for loops)

C++11 cung cấp một cú pháp mới cho vòng lặp for dựa trên khoảng giá trị mà nó làm việc như sau:

Code:
for(auto <biến lặp> : <khoảng giá trị>)
<câu lệnh>;
Ví dụ thay vì viết:
for(auto it=begin(v);it!=end(v);++it)
cout << it;
ta sẽ viết
for(auto it : v)
cout << it;
Các mảng và các lớp có cài đặt các hàm non-member begin() và end() đều có thể dùng theo cú pháp này.

8.4. Các hàm đo thời gian mới
Như đã đề cập ở đầu bài viết, rất nhiều thư viện con của boost được đưa vào chuẩn C++11, và một trong số đó là chrono, thư viện làm việc với các kiểu dữ liệu thời gian, ngày tháng. Để làm việc với thời gian thư viện này cung cấp 3 kiểu đồng hồ: system_clock, gắn với thời gian thực của hệ thống, steady_clock với tính năng gần với thời gian thực hơn (không bị giảm giá trị mà chỉ tăng), và high_resolution_clock, lớp biểu diễn đồng hồ với độ chích xác tốt nhất có thể của hệ thống. Sau đây là một ví dụ về đo thời gian thực hiện chương trình với lớp high_resolution_clock:

Code:
#include <chrono>
#include <iostream>
#include <iomanip>
int main()
{
using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;
auto t0 = high_resolution_clock::now();
// thực hiện tác vụ gì đó ...
auto t1 = high_resolution_clock::now();
milliseconds total_ms = std::chrono::duration_cast<milliseconds>(t1 - t0);
std::cout <<"Thoi gian: " << total_ms.count() << " ms\n";
return 0;
}
8.5. Lớp vector: hàm emplace_back() hiệu quả hơn push_back()
Có thể nói lớp vector là một lớp bản mẫu được sử dụng nhiều và hiệu quả bậc nhất của thư viện STL. C++11 giới thiệu hai hàm mới hiệu quả hơn cho lớp vector (cùng với hai lớp chứa khác là list, deque) có tên là emplace_back và emplace_front. Về mục đích, kết quả của hai hàm emplace_back và emplace_front hoàn toàn giống với push_back và push_front. Về cú pháp cũng có sự tương đồng:

Code:
void emplace_back(Type&& _Val); // move emplace_back
void push_back(const Type& _Val); // copy push_back
void push_back(Type&& _Val); // move push_back
Tuy nhiên hàm các hàm emplace có một phiên bản overloading rất hiệu quả:
Code:
void emplace_back(Args&&… args); // in-place emplace_back
phiên bản này cho phép các tham số được dùng khi khởi tạo các đối tượng của một lớp chứa có thể được dùng theo kiểu in-place, tức là một cách trực tiếp để tạo ra một đối tượng mà lớp chứa quản lý thay vì tạo ra đối tượng rồi mới copy hoặc move giống như hàm push_back. Kết quả thực tế cho thấy hàm emplace_back thường nhanh hơn 20% so với hàm push_back (kết quả này thu được trên các lớp user-defined, còn với các kiểu dữ liệu đơn giản thuộc loại built-in như int, float, double hay complex thì hai hàm có tốc độ thực hiện như nhau).

Để kết thúc chúng ta hãy xem xét hai chương trình nhỏ sau:

chương trình C++ theo chuẩn C++0x:


Code:
#include <iostream>
#include <ctime> // clock functions
#include <vector>
using namespace std;
int main()
{
clock_t startTime = clock();
vector<vector<int> > V; // cú pháp > >, cần có một dấu cách
for(int k = 0; k < 200000; ++k) {
vector<int> vi(1000,k);
V.push_back(vi);
}
clock_t endTime = clock();
cout << "Time is:" << (float)(endTime-startTime)/CLK_TCK << endl;
return 0;
}
chương trình theo chuẩn C++11:

Code:
#include <iostream>
#include <chrono> // clock functions
#include <vector>
#define TEST_C11_COPY 0 // 1 de test push_back copy
#define TEST_C11_MOVE 1 // 1 de test push_back move
using namespace std;
int main()

{
auto startTime = chrono::high_resolution_clock::now();
vector<vector<int>> V;
// không cần dấu cách, trình biên dịch đủ thông minh để hiểu >> không phải một toán tử
for(int k = 0; k < 200000; ++k) {
vector<int> vi(1000,k);
if(TEST_C11_COPY)
V.push_back(vi);

if(TEST_C11_MOVE)
V.push_back(std::move(vi));
}
auto endTime = chrono::high_resolution_clock::now();
auto total = chrono::duration_cast<chrono::milliseconds>(endTime-startTime);
cout << "Time is:" << total.count() << endl;
return 0;
}
Và đây là kết quả thực hiện chương trình (biên dịch với Orwell DevCpp 5.6.1, Gcc 4.8.1):

Chương trình Thời gian thực hiện (mili giây) Ghi chú
Code:
C++0x push_back 1638 C++0x
C++11 push_back copy 648 ISO C++11
C++11 push_back move 578 ISO C++11

Có thể thấy rõ sự cải thiện của tốc độ thực hiện chương trình khi chuyển từ C++0x sang C++11 cũng như sự hiệu quả của việc thực hiện cơ chế move semantics trong bảng trên.

Trong một khuôn khổ của một bài viết, không có nhiều chỗ cho các chi tiết cụ thể và tất cả các tính năng khác của chuẩn C++11, vì vậy muốn biết tường tận hơn các bạn có thể tham khảo trong các tài liệu mà tôi đã liệt kê ở phần đầu. Cuối cùng tôi muốn đưa ra một vài lý do để trả lời cho câu hỏi: Tại sao nên học C++11? Tất nhiên mỗi ngôn ngữ lập trình đều có điểm mạnh riêng của nó nhưng việc học và sử dụng C++ mang lại hai ưu thế sau: thứ nhất, nếu bạn biết C++, việc học một ngôn ngữ lập trình thứ hai sẽ trở nên dễ dàng hơn rất nhiều vì cú pháp, các nguyên lý lập trình của C++ là khởi nguồn cho các ngôn ngữ lập trình hiện đại khác. Thứ hai và quan trọng nhất, lập trình C++ cho phép tạo ra các chương trình có hiệu năng cao nhất một cách hiệu quả nhất. Điều này đạt được là do C++ có một hệ thống cú pháp linh hoạt và có thể sử dụng rất nhiều các thư viện mạnh và ổn định (như Boost, STL, OpenCV, OpenCL, CUDA, AMP, TBB, MKL, OpenBLAS …) cho phép tận dụng tối đa sức mạnh của các hệ thống phần cứng đa nhân, không đồng nhất hiện nay để xử lý các bài toán dữ liệu lớn (như nhận dạng mẫu, học máy, khai phá tri thức, điều khiển tự động ..) ngày càng xuất hiện nhiều hơn và là một xu thế tất yếu của tương lai.

Mặc dù đã dành nhiều tâm huyết và thận trọng trong việc trình bày các khái niệm và các ví dụ liên quan tới chuẩn C++11 trong bài viết nhưng vẫn có thể có những thiếu sót. Rất mong nhận được sự góp ý chân thành của các bạn độc giả, mọi ý kiến, thắc mắc xin gửi về địa chỉ email:
tuannhtn@gmail.com.

Một số thông tin sử dụng trong bài viết lấy từ các nguồn sau:
1. Chỉ số TIOBE đánh giá độ phổ biến của các ngôn ngữ lập trình, http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html.
2. “The most important C++ people… ever”, http://www.artima.com/cppsource/top_cpp_people.html.
3. “C++ and Beyond 2011: Herb Sutter - Why C++?”, http://channel9.msdn.com/posts/C-and-Beyond-2011-Herb-Sutter-Why-C.
4. Boost C++ libraries, http://www.boost.org


Được sửa bởi jackauk ngày Thu May 19, 2016 10:21 pm; sửa lần 2.
jackauk
jackauk
Thành viên thường

Tổng số bài gửi : 63
Điểm danh tiếng : 2
Join date : 16/08/2015
Age : 35
Đến từ : TP Hồ Chí Minh

Về Đầu Trang Go down

Các tiêu chuẩn của ngôn ngữ lập trình C++ Empty Re: Các tiêu chuẩn của ngôn ngữ lập trình C++

Bài gửi by jackauk Wed May 18, 2016 9:52 pm

Dưới đây là bản dịch từ C++11 FAQ của Ngài Bjarne Stroustrup_ người đặt viên gạch đầu tiên cho C++ và dẫn hướng cho quá trình phát triển của C++ từ khi nó được thai nghén cách đây hơn 40 năm.

[markP=0]Mục lục[/markP]
[GoTo=1]__cplusplus[/GoTo]
[GoTo=2]auto -- deduction of a type from an initializer [/GoTo]
[GoTo=3]Range-for statement[/GoTo]
[GoTo=4]right-angle brackets [/GoTo]
[GoTo=5]control of defaults: default and delete [/GoTo]
[GoTo=6]control of defaults: move and copy [/GoTo]
[GoTo=7]enum class -- scoped and strongly typed enums[/GoTo]
[GoTo=8]constexpr -- generalized and guaranteed constant expressions [/GoTo]
decltype -- the type of an expression 10
Initializer lists 10
Preventing narrowing 12
Delegating constructors 13
In-class member initializers 13
Inherited constructors 15
Static (compile-time) assertions -- static_assert 16
long long -- a longer integer 17
nullptr -- a null pointer literal 17
Suffix return type syntax 17
template alias (formerly known as "template typedef") 18
Variadic Templates 19
Uniform initialization syntax and semantics 22
Rvalue references 23
unions (generalized) 25
PODs (generalized) 26
Raw string literals 27
User-defined literals 28
Attributes 29
Lambdas 30
Local types as template arguments 32
noexcept -- preventing exception propagation 32
alignment 34
Override controls: override 34
Override controls: final 35
C99 features 36
Extended integer types 37
Dynamic Initialization and Destruction with Concurrency 37
thread-local storage (thread_local) 37
Unicode characters 37
Copying and rethrowing exceptions 37
Extern templates 38
Inline namespace 38
Explicit conversion operators 39
Algorithms improvements 40
Container improvements 42
Scoped allocators 43
std::array 45
std::forward_list 46
Unordered containers 46
std::tuple 47
metaprogramming and type traits 48
std::function and std::bind 48
unique_ptr 49
shared_ptr 51
weak_ptr 52
Garbage collection ABI 53
Memory model 54
Threads 56
Mutual exclusion 59
Locks 61
Condition variables 63
Time utilities 63
Atomics 65
std::future and std::promise 65
std::async() 67
abandoning a process 68
Random number generation 68
Regular expressions 70
Concepts 70
Concept maps 72
Axioms 72


__cplusplus [markP=1][/markP]

Ở C++11 macro _cplusplus sẽ được đặt giá trị lớn hơn giá trị hiện tại là 199711L

auto -- deduction of a type from an initializer(Suy luận kiểu từ một khởi tạo) [markP=2][/markP]

Xem xét


Code:
auto x= 7;


Ở đây x sẽ có kiểu int vì đấy là kiểu mà nó được khởi tạo. Nói chung, chúng ta có thể viết


Code:
auto x = expression


và kiểu của x sẽ là kiểu của giá trị tính toán từ "expression".
Việc sử dụng  auto để suy luận kiểu của biến từ khởi tạo của nó, rõ ràng là cách hiệu quả nhất khi mà kiểu của biến khó biết chính xác cũng như khó để viết ra. Hãy xem:


Code:

 template<class T> void printall(const vector<T>& v)
 {
 for (auto p = v.begin(); p!=v.end(); ++p)
 cout << *p << "\n";
 }

Ở C++98, chúng ta thường viết


Code:

 template<class T> void printall(const vector<T>& v)
 {
 for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
 cout << *p << "\n";
 }


KHi mà kiểu của biến phụ thuộc rất nhiều vào template của đối số, thực sự rất khó để viết code nếu không có auto. Ví dụ:


Code:
template<class T, class U> void multiply(const vector<T>& vt, const vector<U>& vu)
 {
 // ...
 auto tmp = vt[i]*vu[i];
 // ...
 }

Kiểu của tmp là thứ bạn nhận về từ phép nhân của TU, nhưng chính xác là gì thì khó cho người đọc tính ra, dĩ nhiên là trình biên dịnh tính toán ra cụ thể T và U và nó xử lý chúng.
Tính năng auto được phân biệt sớm nhất để được đề xuất và hiện thực.
Trong C++98 và C99, auto chỉ có nghĩa là "đây là biến cục bộ". Rõ ràng là nó không còn hợp lý trong thực tế nữa. Từ C++11 nó có nghĩa là "khai báo kiểu một cách tự động".

Range-for statement [markP=3][/markP]
Một loạt những câu lệnh cho phép bạn lặp lại thông qua một "range", Đấy là thứ mà bạn lặp lại tương tự nhưu một STL-sequence định nghĩa bởi begin() và end(). Các lớp/ tổ chức chứa dữ liệu tiêu chuẩn có thể  sử dụng như là một "range" ví dụ như là std::string, list, array hay bất kế thứ gì mà mà bạn có thể định nghĩa được begin() và end(), ví như một istream. Ví dụ:


Code:
void f(vector<double>& v)
{
 for (auto x : v) cout << x << '\n';
 for (auto& x : v) ++x; // using a reference to allow us to change the value
}


Bạn có thể đọc là "cho tất cả x thuộc v" thực hiên từ v.begin() lặp lại tới v.end(). Ví dụ khác



Code:
for (const auto x : { 1,2,3,5,8,13,21,34 }) cout << x << '\n';

.

begin() ( và end()) có thể là một thành viên được gọi x.begin() hay một hàm độc lập được gọi begin(x).  Mà phiên bản đầu được ưu tiên.

right-angle brackets [markP=5][/markP]

Hãy xem:


Code:
list<vector<string>> lvs;


Ở C++98 đâu là lỗi cú pháp vì không có khoảng trắng giữa 2 dấu >. C++11 nhận dạng rằng 2 dấu > như là một điểm kết thúc đúng của hai list với các mẫu đối số.
Tại sao điều này từng là một vấn đề? Một trình biên dịch front-end được tổ chức dạng phân tích cú pháp/công đoạn. Đây là một mô hình đơn giản nhất:

+Phân tích từ vựng (cập nhật các mã thông báo từ các ký tự)
+Phân tích cú pháp ( kiểm tra ngữ pháp)
+Kiểu tra kiểu (kiểm tra các kiểu của tên hay dấu hiệu)

Các giai đoạn này là trên lý thuyết và thỉnh thoảng tách rời nhau trong thực tế, do vây bộ phân tích từ vừng xác định >> là một token (thường có nghĩa là dịch bit phải hay là ngõ nhập liệu) mà không quan tâm về ý nghĩa của nó; cụ thể nó không không cho rằng đây là một nguyên mẫu hay mẫu đối số của cấu trúc list. Tuy nhiên, để ví dụ trên "đúng" ba gia đoạn có  nhiều cách để phối hợp. Nhiều trình biên dịch giải quyết vấn đề này bằng các cảnh báo lỗi hợp lệ.

control of defaults: default and delete [markP=4][/markP]
Thuật ngữ thông dụng "prohibiting copying" (cấm sao chép) giờ được thể hiện một cách trực tiếp




Code:
class X {
 // ...
 X& operator=(const X&) = delete; // Disallow copying
 X(const X&) = delete;
 };

Ngược lại, chúng ta có thể nói trực tiếp là chúng ta muốn có các hành vi copy mặc định


Code:
class Y {
 // ...
 Y& operator=(const Y&) = default; // default copy semantics
 Y(const Y&) = default;
 };


Điều  rõ ràng về default (mặc định) là không cần thiết. Tuy nhiên, ý kiến về các tiến trình sao chép và việc một người sử dụng xác định một cách rõ ràng  về hoạt động sao chép là để đưa ra  các hành vi mặc định không phải là hiếm. Để nó cho trình biên dịch để thực hiện các hành vi mặc định thì đơn giản hơn, ít dễ bị lỗi, và thường dẫn đến mã đối tượng tốt hơn.

Cơ chế "mặc định" có thể được sử dụng cho bất kỳ chức năng mà có một mặc định. Cơ chế "delete" có thể được sử dụng cho  một số chức năng . Ví dụ, chúng ta có thể loại bỏ một chuyển đổi không mong muốn như thế này:


Code:
struct Z {
 // ...

 Z(long long);     // can initialize with an long long
 Z(long) = delete; // but not anything less
 };



control of defaults: move and copy [markP=6][/markP]

Mặc định, một class có 5 tiến trình:

+    copy assignment
+  copy constructor
+move assignment
+  move constructor
+   destructor

Nếu bạn khai báo một vài trong số chúng thì bạn phải xem xét hết và định nghĩa rõ ràng hay mặc định từng cái mà bạn muốn. Nói về copy, move, và destruction với các tiến trình có liên quan, hơn việc bản thân tiến trình mà bạn có thể tự do trộn lẫn và kết hợp_ bạn có thể kết hợp tùy ý, nhưng chỉ có một vài kết hợp có ý nghĩa về mặt ngữ pháp.

Nếu bất kỳ move, copy, hoặc destructor được quy định rõ ràng (khai báo, định nghĩa, =default hay =delete) bởi người dùng, thì không hoạt động move được tạo ra mặc định, bất cứ hoạt động copy không khai báo thì được tạo theo mặc định, nhưng điều này không được khuyến khích, do đó không nên dựa vào điều đó. Ví dụ:




Code:
class X1 {
 X1& operator=(const X1&) = delete; // Disallow copying
 };


Đoạn code mặc nhiên không cho phép move của những  phần tử X1. Sao chép khởi tạo được cho phép nhưng mà bị phản đối.




Code:
class X2 {
 X2& operator=(const X2&) = delete;
 };


Đoạn code mặc nhiên không cho phép move của những  phần tử X2. Sao chép khởi tạo được cho phép nhưng mà bị phản đối.




Code:
class X3 {
 X3& operator=(X3&&) = delete; // Disallow moving
 };



Điều này mặc nhiên cũng không cho phép sao chép các X3.


Code:

 class X4 {
 ~X4() = delete; // Disallow destruction
 };


Điều này mặc nhiên cũng không cho phép di chuyển của X4s. Sao chép được cho phép, nhưng bị phản đối.

Tôi đề nghị rằng nếu bạn khai báo một trong những năm chức năng, bạn khai báo rõ ràng cho tất cả. Ví dụ:




Code:
template<class T>
 class Handle {
 T* p;
 public:
 Handle(T* pp) : p{pp} {}
 ~Handle() { delete p; } // user-defined destructor: no implicit copy or move

 Handle(Handle&& h) :p{h.p} { h.p=nullptr; } // transfer ownership
 Handle& operator=(Handle&& h) { delete p; p=h.p; h.p=nullptr; return *this; } // transfer ownership

 Handle(const Handle&) = delete; // no copy
 Handle& operator=(const Handle&) = delete;

 // ...
 };





enum class -- scoped and strongly typed enums [markP=7][/markP]

Một lớp kiểu liệt kê ("new enums","strong enums") đặt ra 3 vấn đề với liệt kê với C++ truyền thống:

* Những enum thông thường có thể chuyển đổi ngầm sang kiểu int, gây ra lỗi khi một ai đó không muốn liệt kê này đóng vai trò như một số integer.
* Những enum thông thường xuất các thành phần liệt kê sang các tầm nhìn xung quanh có thể gây xung đột về tên của liệt kê.
* Loại dữ liệu cơ bản của một enum không thể chỉ định, gây ra nhầm lẫn, vấn đề về tương thích, và làm cho việc khai báo tiếp theo là không có khả năng.

Lớp enum ("strong enums") là một kiểu và tầm nhìn mạnh mẽ:


Code:
enum Alert {green, yellow, orange, red}; // Kiểu liệt kê truyền thống
enum class Color {red, blue};    // kiểu và tầm nhìn mạnh mẽ
// không có xuất các tên liệt kê sang cá tầm nhìn đóng
// không có chuyển đổi ngầm sang kiểu int
enum class TrafficLight { red, yellow, green};

        Alert a = 7;              // error (as ever in C++)
 Color c = 7;              // error: no int->Color conversion

 int a2 = red;             // ok: Alert->int conversion
 int a3 = Alert::red;      // error in C++98; ok in C++11
 int a4 = blue;            // error: blue not in scope
 int a5 = Color::blue;     // error: not Color->int conversion

 Color a6 = Color::blue;   // ok


Được sửa bởi jackauk ngày Sun Apr 02, 2017 9:39 pm; sửa lần 12.
jackauk
jackauk
Thành viên thường

Tổng số bài gửi : 63
Điểm danh tiếng : 2
Join date : 16/08/2015
Age : 35
Đến từ : TP Hồ Chí Minh

Về Đầu Trang Go down

Các tiêu chuẩn của ngôn ngữ lập trình C++ Empty Re: Các tiêu chuẩn của ngôn ngữ lập trình C++

Bài gửi by jackauk Thu May 19, 2016 11:03 pm

Giới thiệu về C++14
Xem chất lượng cao nhất ở trang:
https://channel9.msdn.com/Events/CPP/CppCon-2015/Writing-Good-C-14

Đây là phiên bản youtube:


jackauk
jackauk
Thành viên thường

Tổng số bài gửi : 63
Điểm danh tiếng : 2
Join date : 16/08/2015
Age : 35
Đến từ : TP Hồ Chí Minh

Về Đầu Trang Go down

Các tiêu chuẩn của ngôn ngữ lập trình C++ Empty Re: Các tiêu chuẩn của ngôn ngữ lập trình C++

Bài gửi by Sponsored content


Sponsored content


Về Đầu Trang Go down

Về Đầu Trang

- Similar topics

 
Permissions in this forum:
Bạn không có quyền trả lời bài viết