2021年5月20日 星期四

讀書心得 - C++ Primer (5th Edition) - Chapter 4 (3) - 常用詞彙

常用詞彙

  • arithmetic conversion
    A conversion from one arithmetic type to another. In the context of the binary arithmetic operators, arithmetic conversions usually attempt to preserve precision by converting a smaller type to a larger type (e.g., integral types are converted to floating point).
  • associativity
    Determines how operators with the same precedence are grouped. Operators can be either right associative (operators are grouped from right to left) or left associative (operators are grouped from left to right).
  • cast
    An explicit conversion.
  • compound expression
    An expression involving more than one operator.
  • expression
    The lowest level of computation in a C++ program. Expressions generally apply an operator to one or more operands. Each expression yields a result. Expressions can be used as operands, so we can write compound expressions requiring the evaluation of multiple operators.

讀書心得 - C++ Primer (5th Edition) - Chapter 4 (2) - Type Conversions

Type Conversions

  • Implicit Conversions
    型別轉換,大部分轉換都是看是否能轉換成 int,例如 char, bool, 和 short。
        bool      flag;   char           cval;
        short     sval;   unsigned short usval;
        int       ival;   unsigned int   uival;
        long      lval;   unsigned long  ulval;
        float     fval;   double         dval;
        3.14159L + 'a'; //  'a' promoted to int, then that int converted to long double
        dval + ival;    //  ival converted to double
        dval + fval;    //  fval converted to double
        ival = dval;    //  dval converted (by truncation) to int
        flag = dval;    //  if dval is 0, then flag is false, otherwise true
        cval + fval;    //  cval promoted to int, then that int converted to float
        sval + cval;    //  sval and cval promoted to int
        cval + lval;    //  cval converted to long
        ival + ulval;   //  ival converted to unsigned long
        usval + ival;   //  promotion depends on the size of unsigned short and int
        uival + lval;   //  conversion depends on the size of unsigned int and long
    
  • Explicit Conversions
    用於我們想明確地強制將 object 轉換為其他類型。cast-name\(expression)。(const相關文章1, const相關文章2)
        // static_cast 最常用的 cast-name
        // cast used to force floating-point division
        double slope = static_cast<double>(j) / i;
    
        // const_cast 用來改變 low-level const (point to const)
        const char *pc;
        char *p = const_cast<char*>(pc); // ok: but writing through p is undefinedc
    
        // reinterpret_cast 對其 operand 進行 low-level bit 的重新解釋
        // 要很理解其型別的 bit 組成才會使用
        int *ip;
        char *pc = reinterpret_cast<char*>(ip);
    
cast 基本上就是擾亂型別,建議能不使用就不使用。

讀書心得 - C++ Primer (5th Edition) - Chapter 4 (1) - Expression & Operator

Expression

運算式 (expression) 由運算元 (operand) 與運算子 (operator) 所組成,最終計算產生一個結果 (result)。
  • Order of Evaluation
    想要計算 expression 的話,就必須知道運算子運作及其先後順序。這裡的 * 號就不是 pointer 而是相乘,然後再來是基本的先乘除後加減。
        cout << 5 + 10 * 20 / 2 << endl;        // 105
        cout << 6 + 3 * 4 / 2 + 2 << endl;      // 14
        cout << (6 + 3) *  (4 / 2 + 2) << endl; // 36
        cout << ((6 + 3) *  4) / 2 + 2 << endl; // 20
        cout << 6 + 3 * 4  / (2 + 2) << endl;   // 9
    
  • Lvalues and Rvalues
    有寫過一篇 Lvalues and Rvalues 也可以參考。這裡紀錄此書寫大概的定義,當我們將一個 object 視為 rvalue 使用,使用的是此 object 的值 (contents)。當我們將一個 object 視為 lvalue 使用,使用的是此 object 的「存在」(記憶體位置)。還有一點也很重要,lvalue 可以代替 rvalue,但反之 rvalue 卻不能代替 lvalue。
        int a = 1;     // a = lvalue, 1 = rvalue
        int b = a + 1; // a = lvalue, a = rvalue (lvalue 代替 rvalue)
        int 1 = a;     // Error, 1 != lvalue (rvalue 不能代替 lvalue)
    

Operators

  • Logical and Relational Operators
    Logical Operators 和 Relational Operators 回傳 bool。Logical Operators AND 跟 OR 永遠都會先計算左側再來計算右側。這裡想特別紀錄的是 && 跟 ||,&& 代表只有左側計算出來是 True 才會計算右側,|| 則是只有左側計算出來是 False 才會計算右側。這可以寫出一些流程控制而不是使用典型的 if else。Relational Operators 則是 >, <, >=, <=, ! 這些。
        // 以下這兩個 val 都會被轉換成 bool
        if (val)  { /*  ...  */ } // true if val is any nonzero value
        if (!val) { /*  ...  */ } // true if val is zero
    
        // 有了上面兩個例子,我們看似可以將其改寫成以下寫法
        if (val == true) { /*  ...  */ }
    
        // 這個會產生問題,只要 val 的型別不是 bool
        // 假設 val 是 int,true 會被轉換成 int 也就是 1 跟原先的任何 nonzero value 相去甚遠
        if (val == 1) { /*  ...  */ } // true if val is 1
    
  • Assignment Operators
    最常見就是等號 (=)。Assignment 是 Right Associative,以下有程式碼示範。另外 Assignment Operators 執行順序較低通常會用括弧輔助。
        int ival, jval;
        ival = jval = 0; // ok: each assigned 0
        // 對於右邊的 assignment,jval = 0
        // 對於左邊的 assignment,ival = (jval = 0)
        // jval assigned 0 後,回傳 jval (ival = jval),ival = 0
    
        // 原先邏輯
        int i = get_value();
        while (i != 42) {
            // do something ...
            i = get_value();
        }
        // 改寫,用括弧輔助
        int i;
        while ((i = get_value()) != 42) {
            // do something ...
        }
    
  • Increment and Decrement Operators
    ++ -- 用來做 Increment 和 Decrement,兩個都有 prefix 跟 postfix 的寫法,效果如下程式碼示範。要注意的是要盡量用 prefix 來做運算,因為你通常不會需要做增減前的 value,而 postfix 會 copy 一份增減前的 value 來做回傳。 
        int i = 0, j;
        j = ++i; // j = 1, i = 1: prefix yields the incremented value
        j = i++; // j = 1, i = 2: postfix yields the unincremented value
    
        // 需要 postfix 的場合
        auto pbeg = v.begin();
        // print elements up to the first negative value
        while (pbeg != v.end() && *beg >= 0)
            cout << *pbeg++ << endl; // print the current value and advance pbeg
    
    Operand 運算順序不是固定的,通常不會造成問題,但如果兩邊有用到同一 variable 且用了 Increment 或 Decrement Operators 問題就會浮現。
        // 原先邏輯是把 s 裡的 char 都改成大寫
        // the behavior of the following loop is undefined!
        auto it = s.begin()
        while (it != s.end() && !isspace(*it))
            *it = toupper(*it++);   // error: this assignment is undefined
    
        // 會有兩種結果
        // 第一種輸出 ABCDEFG
        *beg = toupper(*beg);        // 先執行左邊的 Operand,Output: ABCDEFG
        *(beg + 1) = toupper(*beg);  // 先執行左邊的 Operand,Output: aAAAAAA
    
  • Shift Operators (aka IO Operators) Are Left Associative
        cout << "hi" << " there" << endl;
        ( (cout << "hi") << " there" ) << endl;
    
        cout << 42 + 10;   // ok: + has higher precedence, so the sum is printed
        cout << (10 < 42); // ok: parentheses force intended grouping; prints 1
        cout << 10 < 42;   // error: attempt to compare cout to 42!
    

2021年5月13日 星期四

C++ Primer (5th Edition)

讀書心得 - C++ Primer (5th Edition) - Chapter 3 (4) - 常用詞彙

常用詞彙

  • buffer overflow
    Serious programming bug that results when we use an index that is out-of-range for a container, such as a string, vector, or an array.
  • container
    A type whose objects hold a collection of objects of a given type. vector is a container type.
  • index
    Value used in the subscript operator to denote the element to retrieve from a string, vector, or array.
  • iterator
    A type used to access and navigate among the elements of a container.
  • off-the-end iterator
    The iterator returned by end that refers to a nonexistent element one past the end of a container.

讀書心得 - C++ Primer (5th Edition) - Chapter 3 (3) - Array

Array

Array 為資料結構且跟 Vector 很像,都是裝同一 Type object 的 container。但 Array 為固定大小,所以不能增加 element,但也因為如此有時能提供較好的 run-time performance。
  • Initialize
    在 C++ 裡 Array 的 Size 要為 constant expression,也就是 expression 能在 compile time 就算出來。但因為 C 支援在 Array Size 塞變數,所以 G++ 將此寫法視為一個 extension,只要你不在 compile 時強調要遵循 C++ 的 Rule (-pedantic)。
        unsigned cnt = 42;            // not a constant expression
        constexpr unsigned sz = 42;   // constant expression
    
        int arr[10];                  // array of ten ints
        int *parr[sz];                // array of 42 pointers to int
        string bad[cnt];              // error: cnt is not a constant expression
        string strs[get_size()];      // ok if get_size is constexpr, error otherwise
        int a1[3] = {0, 1, 2};        // [0, 1, 2]
        int a2[]  = {0, 1, 2};        // [0, 1, 2]
        int a3[5] = {0, 1, 2};        // [0, 1, 2, 0, 0]
        string a4[3] = {"hi", "bye"}; // ["hi", "bye", ""]
    
    賦予初始值可用 string 來直接 Initialize。
        char a1[] = {'C', '+', '+'};       // list initialization, no null
        char a2[] = {'C', '+', '+', '\0'}; // list initialization, explicit null
        char a3[] = "C++";                 // null terminator added automatically
        const char a4[6] = "Daniel";       // error: no space for the null!
    
    加入 pointer 和 reference 的宣告
        int *ptrs[10];            // ptrs is an array of ten pointers to int
        int &refs[10] = /* ? */;  // error: no arrays of references
        int (*Parray)[10] = &arr; // Parray points to an array of ten ints
        int (&arrRef)[10] = arr;  // arrRef refers to an array of ten ints
        int *(&arry)[10] = ptrs;  // arry is a reference to an array of ten pointers
    
  • Iterate
    這裡紀錄像 Iterator 的方法。arr[10] 是一個不存在的 index 所以只能取 address,所以當 pointer 指到它時就代表此 Array 已結束,類似 begin 跟 end 的方法。但這是較危險的寫法
        int arr[] = {0,1,2,3,4,5,6,7,8,9};
        int *e = &arr[10];
        for (int *b = arr; b != e; ++b)
            cout << *b << endl; // print the elements in arr
    
    有 function 可以直接用。
        int arr[] = {0,1,2,3,4,5,6,7,8,9}; 
        int *pbeg = begin(arr); // pbeg points to the first
        int *pend = end(arr);   // pend points just past the last element in arr
        for (int* p = pbeg; p != pend; ++p)
            cout << (*p) << endl;
    
  • Using an Array to Initialize a vector
        int int_arr[] = {0, 1, 2, 3, 4, 5};
        // ivec has six elements; each is a copy of the corresponding element in int_arr
        vector<int> ivec(begin(int_arr), end(int_arr));
        // copies three elements: int_arr[1], int_arr[2], int_arr[3]
        vector<int> subVec(int_arr + 1, int_arr + 4);
    
  • Initializing the Elements of a Multidimensional Array
        int ia[3][4] = {    // three elements; each element is an array of size 4
            {0, 1, 2, 3},   // initializers for the row indexed by 0
            {4, 5, 6, 7},   // initializers for the row indexed by 1
            {8, 9, 10, 11}  // initializers for the row indexed by 2
        };
        // equivalent initialization without the optional nested braces for each row
        int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
        // explicitly initialize only element 0 in each row
        int ia[3][4] = {{ 0 }, { 4 }, { 8 }};
        // explicitly initialize row 0; the remaining elements are value initialized
        int ix[3][4] = {0, 3, 6, 9};
    
參考資料 :
1.Initializing array with variable vs real number

2021年5月11日 星期二

讀書心得 - C++ Primer (5th Edition) - Chapter 3 (2) - Vector

Library - vector

  • Intro
    vector 是物件的集合,集合裡的物件都是同一 type,且每個物件都有 index 可以用找到該物件。vector 也是 container 和 class template 的一種。
        vector<int> ivec;             // ivec holds objects of type int
        vector<Sales_item> Sales_vec; // holds Sales_items
        vector<vector<string>> file;  // vector whose elements are vectors
    
  • Initialize
        vector<int> ivec;               // initially empty
        // give ivec some values
        vector<int> ivec2(ivec);        // copy elements of ivec into ivec2   
        vector<int> ivec3 = ivec;       // copy elements of ivec into ivec3   
        vector<string> svec(ivec2);     // error: svec holds strings, not ints
        vector<int> ivec(10, -1);       // ten int elements, each initialized to -1
        vector<string> svec(10, "hi!"); // ten strings; each element is "hi!"
    
  • Add
    push_back 會在 run time 的時候執行,會加 object 到 container 的最後端
        vector<int> v2;        // empty vector
        for (int i = 0; i != 100; ++i)
            v2.push_back(i);    // append sequential integers to v2
        // at end of loop v2 has 100 elements, values 0 . . . 99
    
  • Operations
        v.empty()       // Return true if v is empty; otherwise return false.
        v.size()        // Return the number of elements in v.
        v.push_back(t)  // And an element with value t to end of v.
        v[n]            // Return a reference to the element at position n in v
        v1 = v2         // Replaces the elements in v1 with a copy of the elements in v2
        v1 = {a,b,c...} // Replaces the elements in v1 with a copy of the elements in the comma-separated list
        v1 == v2        // v1 and v2 are equal if they have same number of elements and each
        v1 != v2        // element in v1 is equal to the corresponding element in v2
    

Library - iterators

  • Intro
    雖然使用者可以用 [n] (subscripts) 來取得 string 或 vector 的 element。但有一個更通用的用法也就是 iterator。
  • Using
    不像 pointer 不是用 address-of operator 來獲取 iterator,有些型別本身就有成員回傳 iterator。典型就是行別有成員 begin 和 end。begin 指向第一個 element,而 end 指向的是 container 的"結束",也就是說當 container 的 element 數量為 0 時,begin == end。
        // the compiler determines the type of b and e;
        // b denotes the first element and e denotes one past the last element in v
        auto b = v.begin(), e = v.end(); // b and e have the same type
    
  • Operation
        *iter       // Return a reference to the element denoted by the iterator iter
        iter->mem   // Dereferences iter and fetches the member named mem from the
                    // underlying element. Equivalent to the (*iter).mem
        ++iter      // Refers to the next element in the container
        --iter      // Refers to the previous element in the container
    
    利用 begin() != end() 來確認不是空字串
        string s("some string");
        if (s.begin() != s.end()) { // make sure s is not empty
            auto it = s.begin();    // it denotes the first character in s
            *it = toupper(*it);     // make that character uppercase
        }
    
  • Iterates through the elements in container
    這裡用 string 示範,比較要注意的是常用的迴圈終止條件用的是 != end(),而非用往常的 < 。並不是所有 Iterator 都可以支援 < operator,剛好 vector 還有 string 支援而已。
        // process characters in s until we run out of characters or we hit a whitespace
        for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
            *it = toupper(*it); // capitalize the current character
    
  • Type
    如果想要精準宣告 Iterator 的 Type 的話。
        vector<int>::iterator it;        // it can read and write vector<int> elements
        string::iterator it2;            // it2 can read and write characters in a string
        vector<int>::const_iterator it3; // it3 can read but not write elements
        string::const_iterator it4;      // it4 can read but not write characters
    
  • Operations Supported by vector and string Iterators
    上面有提到 vector 跟 string 的 iterator 有支援較多的 operator。
        // The iterators must denote element in, or one past the end of, the same container
    
        iter + n      // Adding (subtracting) an integral value n to (from) an iterator yields an
        iter - n      // iterator that many elements forward (backward) within the container.
    
        iter1 += n    // Compound-assignment for iterator addition. 
        iter1 -= n    // Compound-assignment for iterator subtraction.
    
        iter1 - iter2 // Subtracting two iterators yields the number that when added to the
                      // right-hand iterator yields the left-hand iterator.
    
        >, <=, <, <=  // Relational operators on iterators. One iterator is less than another if it
                      // refers to an element that appears in the container before the referred to 
                      // by the other iterator.
    
    取中間的 iterator
        // compute an iterator to the element closest to the midpoint of vi
        auto mid = vi.begin() + vi.size() / 2;
    
    binary search
        // text must be sorted
        // beg and end will denote the range we're searching
        auto beg = text.begin(), end = text.end();
        auto mid = text.begin() + (end - beg)/2; // original midpoint
        // while there are still elements to look at and we haven't yet found sought
        while (mid != end && *mid != sought) {
            if (sought < *mid)     // is the element we want in the first half?
                end = mid;         // if so, adjust the range to ignore the second half
            else                   // the element we want is in the second half
                beg = mid + 1;     // start looking with the element just after mid
            mid = beg + (end - beg)/2;  // new midpoint
        }
    

2021年5月6日 星期四

讀書心得 - C++ Primer (5th Edition) - Chapter 3 (1) - Namespace

Namespace - using declaration

為了讀取 stdin,程式碼會是 std::cin。 :: 的左邊是告訴 compiler 去哪個 scope 找右邊的 operand。這有時會造成程式碼過於冗長,所以有了 using declaration 讓你更簡單的呼叫 namespace 底下的成員。
    #include <iostream>
    using std::cin;
    int main()
    {
        int i;
        cin >> i;       // ok: cin is a synonym for std::cin
        cout << i;      // error: no using declaration; we must use the full name
        std::cout << i; // ok: explicitly use cout from namepsace std
        return 0;
    }
using declaration 通常不會出現在 header 裡,因為 header 會被複製到各個程式中,會導致所有 include 該 header 的程式用相同的 using declaration 容易造成命名衝突。

Library - string

  • Initialize
        #include <string>
        using std::string;
        string s1;            // default initialization; s1 is the empty string
        string s2 = s1;       // s2 is a copy of  s1
        string s3 = "hiya";   // s3 is a copy of the string literal
        string s4(10, 'c');   // s4 is cccccccccc
    
  • std::cin interaction
    cin 會因為 space 而被分開,以下程式碼若輸入 "Hello World!",則 s1 = "Hello", s2 = "World!"
        string s1, s2;
        cin >> s1 >> s2; // read first input into s1, second into s2
        cout << s1 << s2 << endl; // write both strings
    
    可以用 getline 來讀完整的 "Hello World!"
        string line;
        // read input a line at a time until end-of-file
        while (getline(cin, line))
            cout << line << endl;
        return 0;
    
  • Comparing & Adding Two string
    string 可以使用 ==, != 做比較,也可以用 + 來做字串串接
        string s1 = "hello, ", s2 = "world\n";
        string s3 = s1 + s2;   // s3 is hello, world\n
        s1 += s2;   // equivalent to s1 = s1 + s2
    
  • Iterates through the chars in a string
    可以用 for(auto c : str),也可以用 for(auto &c : str)。&c 是 reference 所以可以改變 str 裡的 char。 
        string str("some string");
        // print the characters in str one character to a line
        for (auto c : str)      // for every char in str
            cout << c << endl;  // print the current character followed by a newline
    

Popular Posts