[c++] 面試題之犄角旮旯 第壹章

記錄C/C++語言相關的問題。html

算法可視化:https://visualgo.net/enios

<data structure and algorithm in c++> By Adam 有免費電子版 c++

 

 

1. while( ) 與 Ctrl+D 

Ctrl+D後,默認仍是要執行完後面的程序。程序員

void multi_input(void)
{
    int sum = 0;
    int value;
    while(std::cin >> value)
    {
        cout << "-" << endl;
        sum += value;
    }
    cout << sum << endl;
}

multi_input()

multi_input()

第一個multi_input()執行中,執行Ctrl+D後:算法

一旦測試失敗,while 終止並退出循環體,執行 while 以後的語句數組

該語句在輸出 sum 後輸出 endl,endl 輸出換行並刷新與 cout 相關聯的緩衝區。安全

最後,執行 return,一般返回零表示程序成功運行完畢。curl

 

2. c++英語學習

聲明 Declarations
定義 Definitions

小括號 parenthesis [pə'renθɪsɪs] 中括號 square brackets 花括號 curly brace

實參
argument
形參 parameter

操縱符 manipulator
...

 

3. 賦值越界

字符

C++ 中,把負值賦給 unsigned 對象是徹底合法的,其結果是該負數對該類型的取值個數求模後的值ide

因此,若是把 -1 賦給 8 位的 unsigned char, 那麼結果是 255, 由於 255 是 -1 對256 求模後的值。函數

 

數字的最大值

float 型只能保證 6位有效數字,可能不夠用;

double 型至少能夠保證 10位有效數字 知足大部分計算的須要。 

size of types

#include<limits>
cout << numeric_limits<double>::max() << endl;

 

字符串的不一樣類型

若是鏈接普通字符串字面值和寬字符串字面值,將會出現什麼結果呢?例如:

// Concatenating plain and wide character strings is undefined
std::cout << "multi-line " L"literal " << std::endl;

其結果是未定義的,也就是說,鏈接不一樣類型的行爲標準沒有定義。

這個程序可能會執行,也可能會崩潰或者產生沒有用的值,並且在不一樣的編譯器下程序的動做可能不一樣。

wchar_t 

L'a' stored in wchar_t.

 

4. 數據間的轉化

unsigned int a = 1;
signed   int b = -1;  // 「局部」大範圍 --> 小範圍
cout << a*b << endl;

4294967295
-1 is promoted to unsigned int, but the binary representation stays
the same: 111111111111111111111111111 (32 bits)
The unsigned value is: 4294967295, therefore
4294967295 * 1 = 4294967295

auto 類型

自動去匹配可能的類型,有解的話就不會報錯。

 

5. 運算符的優先級(precedence)

int i = 2;
int j = (i = 3) * i;
cout << "j = " << j << endl;

j = 9;   // 由於括號優先級高

const

  The primary role of const is to specify immutability.

const int a = 100;
const long &r = a;
// Can refer to an object of a different (but related) type. 但仍是不要這麼作了吧,容易誤會。

Jeff: const int val; int const val 目前編譯器解釋是同樣的。

int i = 0;
int *const p1 = &i; // we can't change the value of p1; const is top-level
const int ci = 42; // we cannot change ci; const is top-level

const int *p2 = &ci; // we can change p2; const is low-level 指向的只是一個特定的類型 const int *const p3 = p2; // right-most const is top-level, left-most is not const int &r = ci; // const in reference type is always low-level 引用的只是一個特定的類型

 

6. Function

Lvalue vs Rvalue

  • rvalue: the content in the vector   -- 內容/值 只會出如今RHS
  • lvalue: address of the vector      -- RHS, LHS均可能出現。

"傳值"的英文叫法是:Call by value or Call by reference.

Return value

Make sure that do not return local object. 

 

注意:Low-level是能夠區分函數的定義的哦:)

 

7. Namespace

reference: http://www.jizhuomi.com/software/289.html

命名空間: 本質上講namespace是對全局做用域的細分。

      將多個變量和函數等包含在內,使其不會與命名空間外的任何變量和函數等發生重命名的衝突。

 1 #include <iostream>  
 2 using namespace std;  
 3   
 4 // 命名空間JiZhuoMi  
 5 namespace JiZhuoMi  
 6 {  
 7     char *szUrl = "www.jizhuomi.com";  
 8 }  
 9   
10 // 命名空間Software  
11 namespace Software  
12 {  
13     char *szUrl = "www.jizhuomi.com/software/";  
14 }  
15   
16 // 釋放命名空間JiZhuoMi和Software  
17 using namespace JiZhuoMi;  
18 using namespace Software;  
19   
20 int _tmain(int argc, _TCHAR* argv[])  
21 {  
22     char *szUrl = "url";  
23     cout << szUrl << endl;  
24     return 0;  
25 } 

 

8. How to print

Reference: http://www.cplusplus.com/reference/ios/ios_base/fmtflags/

具備默認輸出格式

cout 輸出的數字的有效數字位數有默認的限制:6個有效數字。

 1     cout << "[" << 1 << "]" << endl;  2     std::cout << 1331 << std::endl;  3     cout << "[" << 2 << "]" << endl;  4     std::cout << "In hex " << std::hex << 1331 << std::endl;  5     cout << "[" << 3 << "]" << endl;  6     std::cout << 1331.123456 << std::endl;   // 只保留了倆位小數,看來默認是6個有效數字  7     cout << "[" << 4 << "]" << endl;  8  std::cout.setf(std::ios::scientific, std::ios::floatfield);  // 設置了cout的屬性爲科學計數法  9     cout << "[" << 5 << "]" << endl; 10     std::cout << 1331.123456 << std::endl; 11     cout << "[" << 6 << "]" << endl; 12     std::cout << std::setprecision(3) << 1331.123456 << std::endl; // 進一步設置,科學技術法的小數點 13     cout << "[" << 7 << "]" << endl; 14     std::cout << std::dec << 1331 << std::endl;   // 設置回了十進制 15     cout << "[" << 8 << "]" << endl; 16     std::cout.width(8);  // 下一次 ONLY 寫[八]個words. 17     cout << "[" << 9 << "]" << endl; 18     std::cout << 1331 << std::endl; 19     cout << "[" << 10 << "]" << endl; 20  std::cout.setf(std::ios::left, std::ios::adjustfield); 21     cout << "[" << 11 << "]" << endl; 22     std::cout.width(8);  // 自動對齊的一種技巧 23     cout << "[" << 12 << "]" << endl; 24     std::cout << 1331 << std::endl; 25     cout << "[" << 13 << "]" << endl;

16, 設置下次輸出的一個下限。

 

自動對齊輸出

15-22, 自動對齊的技巧。

[1]
1331
[2]
In hex 533
[3]
1331.12
[4]
[5]
1.331123e+03
[6]
1.331e+03
[7]
1331
[8]
       [9]
1331
[10]
[11]
[       12]
1331
[13]
Result

 

9. cin的陷阱

【1】cin.getline(str, len):緩衝區有殘留

當緩衝區中有殘留數據時,cin函數會直接取得這些殘留數據而不會請求鍵盤輸入。

#include <iostream>
using namespace std;
int main()
{
    char str[8];
    cin.getline(str, 5); // input多少,這裏都只從緩衝區 讀取5個。剩下的就自動成爲了下一次輸入。
    cout<<str<<endl;
    cin.getline(str, 5);
    cout<<str<<endl;
    return 0;
}

 

【2】cin >>, 空格會成爲終止

#include <iostream>
using namespace std;
int main()
{
   char str1[10], str2[10];
   cin>>str1;  // 輸入:abcd efg. 第一次讀取字符串時遇到空格則中止.
   cin>>str2;  // 緩衝區有殘留數據,讀入操做直接從緩衝區中取數據。
   cout<<str1<<endl;
   cout<<str2<<endl;
   return 0;
}

看來,比較穩妥的方式是:使用cin.getline(255)這種便可。

 

【3】cin.get(<char>)

空格不會成爲終止(空格可能就是要輸入的字符*_*),Enter能夠。

#include <iostream>
using namespace std;
int main()
{
  char c1, c2;
   cin.get(c1);
   cin.get(c2);
    cout<<c1<<" "<<c2<<endl; // 打印兩個字符
    cout<<(int)c1<<" "<<(int)c2<<endl; // 打印這兩個字符的ASCII值
    return 0;
}

自定義 終止符如何?

#include <iostream>
using namespace std;
int main ()
{
  char ch, a[20];
  cin.get(a, 5 , 'd'); //設置d爲終止符
  cin>>ch;
  cout<<a<<endl;
  cout<<(int)ch<<endl;
  return 0;
}

注意:識別結束符,但不丟棄。

 

【4】靈活清空緩衝區

爲何ch的輸出錯了呢?

#include <iostream>
using namespace std;
int main ()
{
  char ch, a[20];
  cin.getline(a, 5);  // 輸入123456, a: 1234
  cin>>ch;        // ch: 0, but not the ascii of '5'
  cout<<a<<endl;
  cout<<(int)ch<<endl;
  return 0;
} 

因此,輸入一次數據後,最好記得清空「input buffer"。靈活的清空方法以下:

cin.ignore(numeric_limits<std::streamsize>::max(),’\n’);//清除輸入緩衝區的當前行 
cin.ignore(numeric_limits<std::streamsize>::max()); //清除輸入緩衝區裏全部內容 
cin.ignore()//清除一個字符

 

10. 智能指針 - 類型轉換

reference: http://www.jellythink.com/archives/205

C++中的類型轉換分爲兩種:

    1. 隱式類型轉換;
    2. 顯式類型轉換。(這裏的重點)

在標準C++中有四個類型轉換符:static_cast、dynamic_cast、const_cast和reinterpret_cast;

static_cast & dynamic_cast

執行非多態類型的轉換,用於代替C中一般的轉換操做

double myDouble = 3.14; int cast1 = (int)myDouble; // c-style
int cast2 = int(myDouble); int cast3 = static_cast<int>(myDouble); // recommended
  1. 用於類層次結構中,基類和子類之間指針和引用的轉換;
    當進行上行轉換,也就是把子類的指針或引用轉換成父類表示,這種轉換是安全的;【類比:結構體 --> 子結構體】
    當進行下行轉換,也就是把父類的指針或引用轉換成子類表示,這種轉換是不安全的,也須要程序員來保證;【類比:子結構體 --> 外層結構體】
    • dynamic_cast主要用於類層次間的上行轉換和下行轉換,還能夠用於類之間的交叉轉換。【多繼承的狀況】
    • 在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是同樣的;
    • 在進行下行轉換時,dynamic_cast具備類型檢查的功能,比static_cast更安全。(詳見:暫未定關於類的篇章)
  2. 用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum等等,這種轉換的安全性須要程序員來保證;(上述例子)
  3. 把void指針轉換成目標類型的指針,是極其不安全的;

More details: C++多繼承動態交叉轉換dynamic_cast

 

reinterpret_cast

C++裏的強制類型轉換符。讓指針value改變時更安全一些。

// Returns a hash code based on an address
unsigned short hash(void *p) {   unsigned long val = reinterpret_cast<unsigned long>(p);   return ( unsigned short )(val ˆ (val >> 16)); }

當開闢了系統全局的內存空間,須要在多個應用程序之間使用時,須要彼此共享,傳遞這個內存空間的指針時,就能夠將指針轉換成整數值,獲得之後,再將整數值轉換成指針,進行對應的操做。

 

const_cast

用於修改類型的const或volatile屬性。

#include <iostream>
using namespace std;
class CA
{
public:
     CA():m_iA(10){}
     int m_iA;
};
int main() { const CA *pA = new CA; // pA->m_iA = 100; // Error CA *pB = const_cast<CA *>(pA); pB->m_iA = 100;  // 臨時性能夠修改了
// Now the pA and the pB points to the same object cout<<pA->m_iA<<endl; cout<<pB->m_iA<<endl; const CA &a = *pA; // a.m_iA = 200; // Error CA &b = const_cast<CA &>(a); b.m_iA = 200; // Now the a and the b reference to the same object cout<<b.m_iA<<endl; cout<<a.m_iA<<endl; }

通常不要用! 以上例子,本來的const指針無法修改對象的變量,但const_cast以後的新指針,即可以了。但,這樣的操做要慎重!

 

11. 數組

多維數組

打開多維array的正確方式:

    • 體會auto的魅力。
    • begin與end的技巧。
    int ia[3][4] = { 
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12
    };  

    for (auto p = std::begin(ia); p != std::end(ia); ++p) {
        for (auto q = std::begin(*p); q != std::end(*p); ++q) {
            std::cout << *q << " ";
        }
        std::cout << std::endl;
    }

string的遍歷:避免告終束符問題,提倡!

string str = "lolop";
for (auto &c : str) { c = toupper(c); } cout << str << endl;

 

矩陣:數值計算的數組

c++11面向數值計算的數組:valarray

數值計算專列:使用正確的類型,處理的正確問題,也方便代碼閱讀。

    int days[10] = {31, 28, 30, 30, 31, 30, 31, 30, 31, 30};
    valarray<int> vi; 
    valarray<double> di(9);
    valarray<float> fi(3.14, 10);
    valarray<int> vid(days, 5); 
    vid *= 10; 
    cout << vid[3] << endl;

 

動態數組

記得delete就好。

    int n = 10; 
    int *dyn_arr = new int [n];
    for ( int i = 0; i < n; i++)
    {   
        dyn_arr[i] = i;
    }   

    delete [] dyn_arr;

  

12. Operator Overload

1. 在重載下標運算符時(數組符號):不可重載爲友元函數,必須是非static類的成員函數。 why?
 
/* implement */
 
 
2. overload ++ 時,若是是: int a; ++a; ++在前面時,怎麼辦?
答:counter counter::operator ++(int)
  

3. 關於虛析構函數,問:基類中的析構函數通常都設置爲virtual,是麼?不設置的話,會出現什麼問題?

用C++開發的時候,用來作基類的類的析構函數通常都是虛函數。
總的來講虛析構函數是爲了不內存泄露,並且是當子類中會有指針成員變量時纔會使用獲得的。
也就說虛析構函數使得在刪除指向子類對象的基類指針時能夠調用子類的析構函數達到釋放子類中堆內存的目的,而防止內存泄露的。
 
 

 
 「消息傳遞須要消耗時間「,緣由是?
 

 
 
 
 
 
 
暫時放在這裏? 本篇須要大總結和整理.
 

Macros should never be required in C++.
Variables should be defined close to their use.
Don’t use malloc/realloc, use new (or smart pointers)
Minimise use of void*, pointer arithmetic, union and c-style casts
Minimise the use of C-style arrays and strings, use vector/array (from C++11) and string instead.

總之,忘掉C語言。

相關文章
相關標籤/搜索