Những điểm mới trong c++ 11(P2)

      Comments Off on Những điểm mới trong c++ 11(P2)

5. Static assert

Để throw error lúc compile thì có thể dùng #error và #if define.
 

// Static assertion using #error directive
#include <iostream>
using namespace std;
#if !defined(__geeksforgeeks)
#error "Geeksforgeeks hasn't been defined yet".
#endif
int main()
{
    return 0;
}

Nếu dùng #error thì chỉ check dược những cái đơn giản. Không check được những thứ phức tạp ví dụ: sizeof operator.
Trong C++ 11 cung cấp static assertion để bắt được lỗi lúc compile time.
ví dụ:

// CPP code to demonstrate
// static assertion using static_assert
#include <iostream>
using namespace std;
 
template <class T, int Size>
class Vector {
    // Compile time assertion to check if
    // the size of the vector is greater than
    // 3 or not. If any vector is declared whose
    // size is less than 4, the assertion will fail
    static_assert(Size > 3, "Vector size is too small!");
 
    T m_values[Size];
};
 
int main()
{
    Vector<int, 4> four; // This will work
    Vector<short, 2> two; // This will fail
 
    return 0;
}

Output

error: static assertion failed: Vector size is too small!

6. Variadic function template
https://www.geeksforgeeks.org/variadic-function-templates-c/
http://kevinushey.github.io/blog/2016/01/27/introduction-to-c++-variadic-templates/

Nếu muốn dùng 1 hàm mà có arbitrary number parameter thì sẽ dùng đến variadic function.
Ta phải sử dụng kiểu typename… và cài đặt theo đệ quy.. xem ví dụ.


#include <iostream>


/////////////////////////// EXAMPLE 1 ///////////////////////////////////
// Base case
void print() {
	std::cout << "The last" << std::endl;
}

// recusive case
template <typename T, typename... Types>
void print(T var1, Types... var2) {
	std::cout << var1 << std::endl;
	print(var2...);
}

//////////////////////// EXAMPLE 2 ///////////////////////////////////////
// Base case
template<typename T>
T adder(T v) {
	return v;
}
// recusive case
template<typename T, typename...Args>
T adder(T first, Args... args) {
	return first + adder(args...);
}

int main() {

	print(1, 2, 3, 5, "I will print");
	std::cout << adder(1,2,3,5,6);
	system("pause");
	return 0;
}

7. Range-based for loop
https://www.geeksforgeeks.org/range-based-loop-c/
Ở C++ 11 thì chúng ta có thể sử dụng vòng lặp đơn giản hơn dễ đọc hơn.


#include <iostream>
#include <vector>
#include <map>
int main() {
	//Iterating over whole array
	std::vector<int> v = { 0,1,2,3,4 };
	v.push_back(10);
	for (auto n : v) {
		std::cout << n << " ";
	}

	std::cout << std::endl;
	//Iterating over array
	int a[] = {1,2,3,4,5,6};
	for (auto n : a) {
		std::cout << n << " ";
	}

	std::cout << std::endl;
	//Iterating character
	std::string str = "Bui Huu Linh";
	for (auto c : str) {
		std::cout << c << " ";
	}
	std::cout << std::endl;
	// Printing keys and values of a map
	std::map <int, int> MAP({ {1,2}, {3,4}, {5,6}, {7,8} });
	for (auto i : MAP)
		std::cout << "{" << i.first << "," << i.second << "}" << std::endl;

	system("pause");
	return 0;
}

8. List initialization
https://en.cppreference.com/w/cpp/language/list_initialization
Initialize an object from bracket.
Trong C++11 thì initializer list dễ dàng hơn nhiều ví dụ:

struct Foo {
    std::vector<int> mem = {1,2,3}; // list-initialization of a non-static member
    std::vector<int> mem2;
    Foo() : mem2{-1, -2, -3} {} // list-initialization of a member in constructor
};

std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first}; // list-initialization in return statement
}

9. Noexcept
Dùng noexcept khi biết chắc hàm đó ko throw exception gì cả. Cái này đề tăng performance.
Mặc định là được phép exception.

void f() noexcept; // the function f() does not throw

10. Move
C++ giới thiệu 2 member function: move constructor và move assignment operator. Mục đích là để cải thiện performance bằng cách tối ưu việc copy và gán. Hiểu được điều này sẽ tăng khả năng tối ưu chương trình.
https://www.stdio.vn/articles/rvalue-references-va-move-semantics-28

Move Semantics sẽ giúp chúng ta đạt được 4 điều cơ bản:
Giúp loại bỏ những chi phí vô ích khi thực hiện copy dữ liệu từ đối tượng tạm.
Loại bỏ những chi phí “vô hình” khi hàm trả về một đối tượng.
Tối ưu hóa việc copy trong một số trường hợp nhất định, khi chúng ta nắm rõ vòng đời của đối tượng.
Giúp chúng ta thực hiện việc “chuyển quyền sở hữu”.

Move semantics nghĩa là ta thực hiện hành động copy bằng cách move tài nguyên ( chuyển quyền sở hữu tài nguyên từ đối tượng nguôn sang đối tượng đích thay vì phải tạo mới tài nguyên và copy lại.

Khái niệm move constructor sẽ đối lập lại với khái niệm copy constructor.
1 vài ví dụ

vector<VerybigType> v1;
for(int i=0;i<n;++i)
{
VerybigType bigObj;
….
v1.push_back(std::move(bigObj));
}
DynamicArray<int> cloneArrayAndDouble(const DynamicArray<int> &arr){
    .....
}
DynamicArray<int> arr = cloneArrayAndDouble(...); // Định nghĩa move constructor trong trường hợp này để tiết kiệm được bộ nhớ.

#include <iostream>
#include <string>

struct A {
	std::string s;
	A() :s("test") {}
	A(const A& o) :s(o.s) {
		std::cout << "copied" << std::endl;
	}
	A(A&& o) :s(std::move(o.s)) {
		std::cout << "moved" << std::endl;
	}
};
A f(A a)
{
	return a;
}
int main() {
	std::cout << "Trying to move A\n";
	A a1 = f(A()); // move-constructs from rvalue temporary
	A a2 = std::move(a1); // move-constructs from xvalue

	// copy constructor
	A a3 = a2;
	return 0;
}

output:

Trying to move A
moved
moved
copied

11. decltype
dùng từ khóa này nó sẽ tự tìm định nghĩa của biến ví dụ:


#include <iostream>
int main()
{
    int a;
    decltype(a) b;
    b= 10;
    std::cout << "Hello  " << b;
}

12. Từ khóa final và từ khóa override
Từ khóa override chỉ ra rằng hàm này override hàm cha. Nếu ko có hàm cha để override thì trình biên dịch sẽ báo lỗi. Giúp phát hiện sớm lỗi từ lúc
compile time.


    class B
    {
    public:
    	virtual void f(short)
    	{
    		std::cout << "B::f" << std::endl;
    	}
    };
     
    class D : public B
    {
    public:
    	virtual void f(short) override
    	{
    		std::cout << "D::f" << std::endl;
    	}
    };

Từ khóa final để chỉ ra các lớp kế thừ không được phép override lại hàm đó.

    class B
    {
    public:
    	virtual void f(int) final
    	{
    		std::cout << "B::f" << std::endl;
    	}
    };
     
    class D : public B
    {
    public:
    	virtual void f(int) // Compile error because overriding final function
    	{
    		std::cout << "D::f" << std::endl;
    	}
    };

13. lambdas expression
https://www.stdio.vn/articles/9-tinh-nang-quan-trong-trong-cpp11-569

Hàm không tên hay hàm vô danh. Cú pháp lambdas expression có cú pháp như sau:
[Phạm vi sử dụng biến](Tham số)->Kiểu giá trị trả về{Thân hàm}

Phạm vi sử dụng biến
[=] pass by value
[&] pass by reference
[this] cho phép sử dụng tất cả các biến trong phạm vi class.
[] có thể để rỗng.
[a,&b] có thể truyền nhiều biến.

Xem ví dụ dưới đây để hiểu rõ về lambdas expression

#include <iostream>
#include <string>
int main() {

	//1. Không phạm vi sử dụng biến, Không tham số, không giá trị trả về, không cần gán
	[] {
		std::cout << "Example1"<<std::endl;
	}();
	//2. Không phạm vi sử dụng biến, Không tham số, không giá trị trả về, có gán.
	auto f2=[] {
		std::cout << "Example2" << std::endl;
	};
	f2();
	//3. Không phạm vi sử dụng biến, Có tham số đầu vào
	auto f3 = [](const std::string& s) {
		std::cout << s << std::endl;
	};
	f3("Hello lambdas");
	//4. Cho phép sử dụng 2 biến x và y
	int x = 10;
	int y = 11;
	auto f4 = [x, &y] {
		std::cout << x << " " << y << std::endl;
		y++;
	};
	f4();
	std::cout << "y after change: " << y << std::endl;
	//5. Có return tham số
	auto f5 = []()->int {
		return 10;
	};
	std::cout << "return of f5() = " << f5() << std::endl;
	return 0;
}

Lambdas thường được dùng khi truyền thẳng hàm vào 1 hàm khác( Có con trỏ hàm là đầu vào) mà không cần chỉ rõ hàm đó ra.

14. std::thread
https://www.geeksforgeeks.org/multithreading-in-cpp/
Trước C++11 thi ta dùng pthread.h (POSIX) trên Linux hoặc windows.h trên windows bản thân ngôn ngữ không hỗ trợ thread ==> Gây ra sự bất tiện.
Với C++11 thì bản thân ngôn ngữ hố trọ thread với thư viện std::thread.

#include<iostream>
#include <thread>

void SaySomething(const char* msg) {
	std::cout << msg;
}
int main() {

	std::thread task(SaySomething, "Hello std_thred");
	task.join();
	return 0;
}