วันพฤหัสบดีที่ 6 กรกฎาคม พ.ศ. 2560

แนะนำ C++11 - มีอะไรใหม่ มีอะไรดี

แนะนำ C++11 - มีอะไรใหม่ มีอะไรดี
ก่อนอื่นหลายคนอาจจะมีคำถามที่ว่าแล้ว C++11 คืออะไร? ต่างจาก C++ ธรรมดายังไง? คำตอบก็คือว่า C++11 เป็นมาตรฐานของ C++ ตัวใหม่ครับ ซึ่งเป็นมาตรฐานที่ออกมาในปี 2011 คือหมายถึงเถียงกันเสร็จเรียบร้อยแล้วว่าจะให้มีหรือไม่มีอะไรบ้าง อย่างไร ซึ่งถ้าย้อนกลับไปตอนแรกเหมือนมาตรฐานนี้จะใช้ชื่อแบบลำลองว่า C++0x นั่นไม่แน่ใจว่าจะเสร็จในปีไหนแต่คงมี 200x นี่แหละ ซึ่งพอเอาเข้าจริงก็ลากยากมาถึงปี 2011 เลยทีเดียว ถึงขนาดมีคนเล่นมุกว่า 0x ที่ว่าคือ 0B (เลขฐาน 16) นั่นเอง (ฮา)

ตอนที่กำลังเขียนอยู่นี่ก็ต้นปี 2016 เข้าไปแล้ว จริงๆแล้วมันก็มีมาตรฐานใหม่ออกมาเพิ่มอีกแหละครับคือ C++14 กับ C++17 แต่ว่าคราวนี้เรามาดูกันก่อนว่าไอ้เจ้า C++11 นี่มันมีอะไรต่างจากเดิมมั่ง มันเจ๋งขึ้นจริงๆ รึเปล่า?

ก่อนที่จะใช้ C++11 เนี่ยก็จำเป็นที่จะต้องใช้ตัวคอมไพเลอร์ที่ Implement ฟีเจอร์ต่างๆของ C++11 ได้ทั้งหมดแล้ว ซึ่งหาไม่ยากเลยครับ g++, clang ตัวใหม่ หรือจะเป็นคอมไพเลอร์ของ Intel และ Microsoft ก็ใช้ได้หมดแล้ว สามารถดูรายละเอียดว่ามีตัวไหนใช้อะไรได้บ้างที่ C++ compiler support เลยครับ

ทีนี้เรามาดูกันเลยดีกว่าว่ามันมีอะไรที่ทำให้ชีวิตดีขึ้นบ้าง
1. auto

มันคือการที่เราสามารถกำหนดชนิดตัวแปรเป็น auto แล้วให้คอมไพเลอร์ไปหาเองว่ามันควรจะเป็นชนิดอะไรครับ พูดแล้วเห็นภาพยาก ลองดูตัวอย่างดีกว่า
auto x = 20;
auto y = 4.5;
auto z = 'A';

จากตัวอย่างบรรทัดแรกคือ เราไม่ต้องประกาศตัวแปร x เป็นชนิด int ให้เห็นแต่ว่า ใช้ auto ไปเลย คอมไพลเอร์จะเดาชนิดให้เองจาก ชนิดของด้านขวามือ ซึ่งก็คือ int เพราะมันเห็นว่า 20 เป็น int ครับ ตัวแปร y ก็จะหลายเป็น double และ z ก็จะเป็น char แน่นอนว่าคีย์เวิร์ด auto ก็มีข้อจำกัดอยู่ เช่นถ้าเราไม่มีการกำหนดค่ามันก็จะเดาชนิดให้ไม่ออกครับ เช่นประกาศ auto s; แบบนี้ไม่ได้แน่นอนเพราะไม่มีชนิดในมันอ้างอิง (ยังไงตัวแปรใน C++ ก็ต้องกำหนดชนิดข้อมูล เหมือนเดิมครับ เพียงแต่ว่าเราแค่ปล่อยให้มันไปหาเองได้ว่าควรจะเป็นชนิดอะไร)

ตัวอย่างเมื่อกี้เป็นแค่ตัวอย่างให้เห็นภาพครับ ยังไม่ใช่ประโยชน์ที่แท้จริงเท่าไหร่ เพราะมันดูไม่ช่วยอะไรเท่าไหร่ มันจะช่วยได้มากในกรณีต่อไปนี้ครับ
vector<int> v;
/* คำสั่งอื่นๆ */
vector<int>::iterator it = v.begin();

vector< map<int,string> > mapv;
/* คำสั่งอื่นๆ */
map<int,string> m = mapv[5];

การประกาศชนิดของ iterator หรือว่าชนิดที่เป็น template ทั้งหลายจะสูญเสียพลังงานในการพิมพ์มาก เราก็จะเปลี่ยนเป็นใช้ auto แทน ซึ่งเสียเวลาพิมพ์น้อยกว่า โค้ดก็อ่านง่ายสบายตากว่าเดิมเยอะ เช่นแบบนี้ครับ 
vectorint v;
/* คำสั่งอื่นๆ */
auto = v.begin();

vector< map<int,string> > mapv;
/* คำสั่งอื่นๆ */
auto m = mapv[5];

 2. แก้ไขการ parse <>

อันนี้นึกไม่ออกว่าจะเรียกมันว่าอะไร แต่ C++ มีข้อจำกัดที่โคตรจะน่ารำคาญอย่างนึงคือเวลาเขียนชนิดที่เป็น template ซ้อนกันจะต้องเว้นวรรคระหว่าง > อย่างน้อย 1 ทีครับ เช่น ถ้าเราอยากจะสร้าง matrix ขึ้นมาอันนึงเราอาจจะใช้ vector<vector<int>> mat; แต่ว่าเขียนแบบนี้ถือว่าผิด เพราะคอมไพเลอร์จะเข้าใจว่าไอ้ >> ติดกันนี่คือโอเปอเรเตอร์ >> ซึ่งถ้าจะให้ถูกจะต้องเขียนว่า vector< vector<int> > mat; เพื่อกันมันสับสน

แต่ว่า C++11 ไม่โง่แล้วครับ เขียนไปได้เลย vector<vector<int>> มันไม่มึนแล้ว ชีวิตสดใสขึ้นเยอะเลย

 3. ลูป for แบบ range

ภาษาสมัยใหม่มันก็มีกันหมดแล้วล่ะ ก็เลยเป็นอีก 1 ฟีเจอร์ที่ขาดไปไม่ได้ จากที่แต่ก่อนเราอาจจะต้องวนลูปตัวอาร์เรย์หรือ containner ต่างๆ แล้วค่อย get ค่ามาจากการใช้ index แบบนี้

vector<int> v;
for (int i = 0; i < v.size(); ++i) {
    int x = v[i];
    /* เอาค่า x ไปทำอะไรซักอย่าง */
}

for (vector<int>::iterator it = v.begin();
     it != v.end(); ++it) {
    int x = *it;
    /* เอาค่า x ไปทำอะไรซักอย่าง */
}

พอเห็นแล้วก็จะรู้สึกว่ายาวเหลือเกิน จริงๆมันมันเขียนแบบสั้นๆ แต่ได้ความหมายเหมือนเดิม ด้วยการใช้ลูปแบบ range ตามนี้เลยครับ

for (int x : v) {
    /* เอาค่า x ไปทำอะไรซักอย่าง */
}

หรือจะเอา auto มาใช้ก็ได้ เกิดถ้าชนิดของ element ใน container มันยาว

vector< map<int,string> > mapv;
for (auto m : mapv) {
    /* เอาค่า m ไปทำอะไรซักอย่าง */
}

 4. List initialization

อีกตัวที่ผมรู้สึกว่ามันควรจะมีมานานมากแล้ว เพราะเขียน C++ แล้วอึดอัดเหลือเกินกับปัญหาการกำหนดค่าให้ container ลองดูตัวอย่างต่อไปนี้นะครับ

int a[5] = {1,2,3,4,5}; /* ทำได้ */
vector<int> v = {1,2,3,4,5}; /* ทำไม่ได้ */

/* ถ้าจะได้ต้องเอาอาร์เรย์มากำหนดให้มันอีกต่อนึงแบบนี้ */
vector<int> v(a,a+5);

ปัญหาก็ประมาณนี้แหละครับ คือมันกำหนดค่าเริ่มต้นยากมาก ดังนั้นใน C++11 ก็เลยยอมให้ทำได้เรียบร้อยแล้ว  
vector<int> v = {1,2,3,4,5}; /* ทำได้ใน C++11 */
vector<int> v2{1,2,3,4,5}; /* แบบนี้ก็ได้ (C++11) */

/* กำหนดค่าไปแล้วอยากเปลี่ยนกลางอากาศก็ทำได้ สุดยอด!! */
v = {100,200,300};

ผมก็สุดยอดไปงั้นแหละครับ พวกภาษาสมัยใหม่มันเขียนได้อยู่แล้ว แต่ C++ มันทำไม่ได้มาอย่างนาน พอได้แล้วมันก็ต้องดีใจบ้าง (ฮา) นอกจากนี้ยังกำหนดค่าแบบ nested ได้ด้วยนะ อย่างเช่นพวก map

   
map<int,string> m = {
    {1, "one"},
    {2, "two"},
    {3, "three"}
};



5. Lambda functions

Labmda functions ถ้ามองผ่านก็คือฟังก์ชันที่ไม่มีชื่ออันนึง แต่จริงๆแล้วมันมีอะไรมากกว่าฟังก์ชันธรรมดาครับ มันมีส่วนที่เรียกว่า capture ซึ่งสามารถนำ context ภายนอก เช่นตัวแปรต่างๆเข้าไปใช้ในฟังก์ชันที่สร้างขึ้นได้ ซึ่งขอไม่ลงลึกนะครับ ไปดูตัวอย่างการใช้แบบง่ายๆกันก่อนดีกว่า

   
bool moreThan10(int x) { return x>10; }

vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90};
int c = count_if(v.begin(), v.end(), moreThan10);

ฟังก์ชัน count_if ใน <algorithm> ใช้สำหรับนับว่าใน container มีข้อมูลกี่ตัวที่ตรงกับเงื่อนไขที่กำหนดให้ ซึ่งเราจะต้องส่งฟังก์ชันสำหรับกำหนดเงื่อนไขเข้าไปตัวนึง ทีนี้ Lambdas จะมีประโยชน์ตรงนี้แหละครับ คือมันสามารถสร้างฟังก์ชันที่ไม่มีชื่อแล้วส่งเข้าไปตรงนั้นได้เลย

   
vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90};
int c = count_if(v.begin(), v.end(), [](int x){return x>10;});

ซึ่งมันจะเหมาะมากกับฟังก์ชันแบบที่ใช้ครั้งเดียวแล้วก็เลิกลากันไป ไม่ต้องมาประกาศให้กวนใจกันอีกครับ

ตรง [] คือส่วนของ capture ที่บอกว่าจะเอาตัวแปรอะไรเข้าไปใช้ในฟังก์ชันบ้าง ซึ่งในที่นี้เป็นว่างเพราะเราไม่เอาอะไรเข้าไปเลย (int x) อันนี้ก็พารามิเตอร์ของฟังก์ชันเฉยๆ

สมมติว่าอยากให้เช็คว่ามีกี่ตัวบ้างที่มีค่ามากกว่า q

   
vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90};
int q = 8;
int c = count_if(v.begin(), v.end(), [q](int x){return x>q;});

ตรง capture ก็ใส่ตัวแปร q เข้าไป เพื่อบอกว่าให้เอาตัวแปร q ไปใช้ในฟังก์ชันด้วย ซึ่งความหมายของคำสั่งดังกล่าวคือนับจำนวนของข้อมูลใน vector v ที่มากกว่า q (ซึ่งมีค่าเท่ากับ 8)

จาก concept ของ lambdas ทำให้ประกาศตัวแปรฟังก์ชันใน C++11 ได้ง่ายขึ้นมากครับ (เมื่อก่อนมันก็ทำได้แต่ลำบาก) เช่นจากตัวอย่างแรกสุด เราก็จะได้โปรแกรมใหม่หน้าตาแบบนี้ ใช้ชนิดเป็น auto ไปได้เลยครับ
   
auto moreThan10 = [](int x){return x>10;};
int c = count_if(v.begin(), v.end(), moreThan10);

 อื่นๆ

    เพิ่มฟังก์ชันที่เกี่ยวกับ Regular Expression ใน <regex>
    เพิ่มฟังก์ชันที่เกี่ยวกับการสุ่มใน <random>
    Raw string literals ที่เขียน string literal แบบหลายบรรทัดได้ง่ายขึ้น
    คีย์เวิร์ด override ที่เอาไว้บอกว่าฟังก์ชันนี้เป็นการ overriding และคีย์เวิร์ด final ที่ทำให้ฟังก์ชันไม่สามารถ overriding ต่อได้ (หรือกำหนดให้ class ไม่สามารถ inherite ต่อได้) 
    มีฟังก์ชันใหม่ๆอีกมากมาย, smart pointer, enum แบบใหม่, tuple, การจัดการ thread, atomic operator และอื่นๆอีกมากมายครับ

สรุปง่ายๆคือถ้าใช้ C++ อยู่ ลองมาใช้ C++11 กันดูครับ มันสะดวกขึ้นเยอะเลย

ไม่มีความคิดเห็น:

แสดงความคิดเห็น

Set MongoDB in the windows path environment

  Let’s set MongoDB in the windows environment in just a few steps. Step 1: First download a suitable MongoDB version according to your mach...