###C/C++筆記

點擊查看Evernote原文

#@author:       gr
#@date:         2014-07-20
#@email:        forgerui@gmail.com

c/c++筆記。
Life is too short to learn C++.程序員

1、malloc與calloc區別

1. malloc不初始化分配的內存,已分配的內存中能夠是任意的值. 
    calloc 初始化已分配的內存爲0

 2. malloc返回的是一個對象
    calloc返回的是一個數組

2、static做用

  1. 限制了變量的做用域
  2. 改變了變量的存儲域

3、內存釋放

1. new --> delete
 2. malloc --> free

4、棧和堆

1. 棧是由編譯器自動分配釋放的,堆是程序員本身申請釋放
 2. 棧有默認大小(1M),是連續的空間,比堆要空間小,堆不連續,空間大
 3. 申請 棧: int a;比較快 堆: malloc(1024); new() 比較慢

5、size_t

1. size_t是標準C庫中定義的,應爲unsigned int,在64位系統中爲 long unsigned int。 
 2. 在C++中,設計 size_t 就是爲了適應多個平臺的 。size_t的引入加強了程序在不一樣平臺上的可移植性。

6、sizeof

1. char *p; sizeof(p); sizeof(*p);  //p指針的大小爲4,*p的大小是char型,爲1

7、explicit

1. C++中的explicit每每用來修飾構造函數包括拷貝構造函數,通常構造函數有兩種功能,1.是構造器,2.是隱藏且默認的類型轉換操做符。爲使構造函數只作爲構造器,聲明爲explicit,則必須顯式調用構造函數才行,即A a(),不能使用A a = 1。

8、純虛函數

1. 若是函數中有純虛函數就是抽象類,須要子類進行重寫才能進行實例化,而只有virtual成員函數的話,能夠實例化。

9、C++中的靜態綁定和動態綁定

1. 靜態綁定:在聲明時採用的類型,發生在編譯期。
 2. 動態綁定:目標所指的類型;在運行期決定。 
 3. 靜態類型在聲明時就肯定了,沒法修改;動態綁定能夠在程序中修改,指向其它類型。
 4. no-virtual函數是靜態綁定的,即非虛函數根據聲明調用;virtual函數是動態綁定的,即虛函數是根據定義決定的。
 5. 只有虛函數才使用的是動態綁定,其餘的所有是靜態綁定。目前我尚未發現不適用這句話的,若是有錯誤,但願你能夠指出來。
 6. 當缺省參數和虛函數一塊兒出現的時候狀況有點複雜。虛函數是動態綁定的,可是爲了執行效率,缺省參數是靜態綁定的。

10、C++中的聲明和定義

1. 能夠屢次聲明,但只能定義一次
 2. 在函數外部聲明同時會定義,在函數內部聲明不會定義。
 3. 在函數外部可使用extern int a;表示只聲明不定義。但extern int a = 1;這樣也會定義。

11、const問題

有下面的一段代碼:算法

class Rational{
 
private:
    int numer, deno;
public:
    Rational(int n = 0, int d = 1);
    int num()const {return numer;}
    int den() {return deno;}
};
 
Rational operator* (const Rational& l, const Rational& r){
    return Rational(l.num() * r.num(), l.den() * r.den());
}

l.num()r.num()是沒問題的,而l.den()r.den()是存在問題的。由於num()是個const,而den不是const,當把對象看成pass-by-reference-to-const時,它裏面的操做必須保證不得改變成員的值,因此,這個函數中調用對象的成員函數也必須是const的,因此要把den()函數改成const函數。centos

const在C語言中是表示道義上保證變量的值不會被修改,並不能實際阻止修改,經過指針能夠修改常變量的值,可是會出現一些不可知的結果。幾種狀況不一樣,咱們一個一個來看。數組

一、直接賦值安全

const int a = 3;
a = 5;
// const.c:6:2: error: assignment of read-only variable ‘a’
這種狀況不用多說,編譯錯。

二、使用指針賦值,@孫健波提到的方法,在gcc中的warning,g++中出現error,是由於代碼寫得不對,由非const的變成const不用顯式的轉換,const變爲非const須要顯式轉換,這種狀況應當使用顯式的類型轉換。數據結構

const int a = 3;
int* b = (int*) &a;

printf("a = %d, *b = %d\n", a, *b);
*b = 5;
printf("a = %d, *b = %d\n", a, *b);

運行結果(注:使用msvc編譯的結果一致):多線程

$ gcc const.c
$ ./a.out
a = 3, *b = 3
a = 5, *b = 5

$ g++ const.cpp
$ ./a.out
a = 3, *b = 3
a = 3, *b = 5

這裏使用g++編譯時,a的值之因此沒有改變,是由於編譯時a是常量,而後被編譯器編譯爲當即數了。所以對使用b指針修改不會更改a的值。app

值得注意的是,若是a被定義爲全局常變量,使用指針修改會引起segment fault。dom

在函數的原型中,咱們也經常使用const修飾指針,表示函數的實現者在道義上不會去修改這個指針所指向的空間。例如咱們熟知的strcpy函數,原型以下:

char* strcpy(char* dst, const char* src);

傳入的參數src類型是const char*,表示函數內部實現不會修改src所指向的空間。之因此說是道義上,是由於在內部經過上述指針強制類型轉換的方式能夠修改該空間的值。另外,若是咱們聲明瞭咱們不會修改傳入指針的所指向的空間,那麼咱們也不該當去修改這塊空間,由於這個傳入的指針可能會是一個不可寫的內存,而後出現段錯誤。

12、構造函數的默認參數

當類的構造函數有默認參數時,即可以匹配<=參數個數的構造函數,沒有給出的參數使用默認值初始化。

十3、當以pass-by-value傳遞參數時,須要考慮拷貝構造函數是否正確。

十4、類體中的函數自動內聯

類體中定義的成員函數,若是簡單,編譯器會自動變爲內聯函數。

十5、頭文件(詳見 Topic 二十二)

在頭文件能夠聲明變量,不能夠定義變量,即必須加上extern,不然一旦這個頭文件被兩個源文件#include,便會報重定義的錯誤(由於#include後便會在多處定義)。若是實在想定義一個全局變量,應該在cpp中定義,這樣纔不會報錯。頭文件的做用是提供聲明,方便別人進行編譯,切不可在頭文件中進行定義。
但能夠在頭文件中定義下面三個東西:類,const變量(只初始化一次),內聯函數, 模板函數。

十6、頭文件與重複定義

頭文件衛士不能解決變量重定義的問題,它的目的是要保護嵌套的包含指令中的內部連接屬性的名稱不被重複定義。它避免了在一個程序文件中重複include兩次,但在另外一個程序文件中仍是能夠include這個頭文件,因此若是在頭文件定義變量,即便使用了頭文件衛士,仍是會有重定義的問題。

十7、OpenCV的Debug與Release

一直讀入圖片出錯,把Debug修改爲Release版本就解決了,估計是OpenCV中的dll調試與運行版本問題。

十8、二維數組delete

二維數組的delete要進行兩次刪除。

// Definition
double** m_matrix;
// Release
if ( m_matrix != NULL ) {
    // free arrays
    for ( unsigned int i = 0 ; i < m_rows ; i++ ) {
        delete [] m_matrix[i];
    }
    delete [] m_matrix;
}
m_matrix = NULL:

十9、 matrix(x, y)的實現

定義一個Matrix類,初始化對象Matrix matrix;實現下面的兩個操做。

//兩種功能, 一個是賦值,一個是取值
matrix(x, y) = a;
cout << matrix(x,y);

實現須要使用operator ()操做符。

//賦值,返回的是引用,能夠修改其值
template <typename T>
inline T& operator () (int i, int j){
    return matrix[i][j];
}
//取值,返回的是const
template <typename T>
inline const T& operator() (int i, int j) const{
    return matrix[i][j];
}

二10、 list vs vector

vector 可使用下標訪問,list 不能用下標的形式訪問。
vector遍歷刪除:

vector<Track>::iterator it1 = trajs.begin();
while (it1 != trajs.end()){
    if (it1->isLost()){
        trajs.erase(it1);
    }else{
        it1++;
    }
}

list 遍歷刪除:

vector<Track>::iterator it1 = trajs.begin();
while (it1 != trajs.end()){
    if (it1->isLost()){
        // For the vector erase the element, so it1--
        cout<<"delete: "<<it1->getId()<<endl;
        it1 = trajs.erase(it1);
    }else{
        it1++;
    }
}

二11、sprintf,sscanf

sprintf:把變量打印到字符串中,從而得到數字的字符形式(能夠實現將整形轉換成字符型)

int value = 10;
char str[128];
// 結果爲: "the result of value is 10",將數值轉換爲字符
sprintf(str, "the result of value is %d", value);

sscanf: 從一個字符串中讀進與指定格式相符的數據. 格式能夠是整型數據等。

//str="ab",取到非a-z爲止
sscanf("ab123D", "%[a-z]", str);
//str=空,取到a-z爲止,由於第一個就是a,因此不取
sscanf("ab123D", "%[^a-z]", str);
//str="DE12",取到a-z爲止
sscanf("DE12ab123D", "%[^a-z]", str);

二12、頭文件

歸納: 將聲明放在頭文件中,實現放在cpp中。

  • 全局變量,全局函數聲明放在頭文件中,實現放在cpp中。
  • 將類、結構體的聲明,定義放在頭文件中。
  • 類成員函數聲明放在頭文件中,實現放在cpp中。
  • inline相關的函數、template相關的函數和類 實現放在頭文件中。
  • static const修飾的變量的初始化放在頭文件中,類中static變量的初始化放在cpp中,const的初始化放在初始化列表中。

二十3、比較與賦值耗時

比較耗時長
比較有一個讀a和b值得過程,而後比較要創建一個新的區間存儲比較結果,
而複製只是讀取b的值,而後存儲到a的位置,根本不須要讀取a原來的值。

二十4、奇怪的undefined reference

typedef struct  {
    int nFeatures;
    KLT_Feature *feature;
    void WriteToImage(const std::string imgName, const cv::Mat &img) const;
}  KLT_FeatureListRec, *KLT_FeatureList;

定義的結構體有KLT_FeatureListRec*KLT_FeatureList,但其中成員函數WriteToImage實如今cpp文件中,以下使用會形成undefined reference

KLT_FeatureList fl;
fl->WriteToImage(std::string(filename),img);

但把WriteToImage的實現放在struct裏,這時即可以正常編譯。應該是KLT_FeatureList沒法匹配到KLT_FeatureListRec的成員函數。

二十5、free,delete

free以後變爲野指針,要置爲NULL

二十6、默認析構函數

默認析構函數不會釋放類型爲內置指針的成員變量指向的空間。

二十7、默認拷貝構造函數

  1. 默認拷貝構造函數對指針類型進行地址複製,即多個指針指向同一個地址,若是須要拷貝到新的空間,須要本身實現拷貝構造函數。
  2. 拷貝構造時,複製「對象內的全部成員變量」 及 「全部base成分」,能夠經過Child::operator=(const Parent& rhs)去調用子類拷貝構造函數,複製base成分

    class Children{
     private:
         int x;
     public:
         Children& operator=(const Children& rhs){
             x = rhs.x;
             return *this;
         }
     };
     class Parent : public Children{
     private:
         int y;
     public:
         Parent& operator=(const Parent& p){
             y = rhs.y;
             //必定要!!!調用子類operator=,拷貝base成分
             Children::operator=(p);
             return *this;
         }
     };

二十8、類成員變量的初始化

  1. 普通變量能夠在初始化列表初始或在構造函數中賦值
  2. const變量只能在初始化列表中初始化。
  3. static變量只能在類外初始化,且不能在函數中定義,必須在全局定義。若是不初始化,直接使用就只有聲明沒有定義,會報錯,vs中會報沒法解析的外部符號。
  4. const static靜態常量能夠直接在類的定義中初始化
  5. Reference類型在初始化列表中初始化。

有三種包括引用const基類要經過初始化列表初始化。

注意:
C++11中能夠爲數據成員提供一個類內初始值,便可以在類中直接初始化數據成員,沒有初始值的成員將被默認初始化。

二十9、拷貝構造函數

拷貝構造函數第一個參數是自身類類型(且必須是引用類型,傳值會致使死循環),後面的參數都應該提供默認值。

三10、拷貝構造、拷貝賦值

拷貝構造函數和賦值操做符之間不能相互轉換,若是調用的函數沒有實現,則會調用編譯器默認生成的函數。

A a = b;    //調用的是拷貝構造,等同於A a(b);
A a;
a = b;      //調用的是拷貝賦值

三11、 cout char*

若是想要輸出指針地址,使用static_cast<const void*>轉型。

char* str = "Hello world";
cout << str << endl;        //輸出的是字符串,不是地址
cout << static_cast<const void *>str << endl;           //輸出字符指針的地址

三12、 在類的成員函數中能夠訪問同類型實例的私有變量

類成員變量的訪問權限是編譯器強加的,只要編譯器能夠找到符號,就能夠經過編譯。

在類中的同類實例的私有變量符號能夠在當前類域中查找到,因此能夠經過編譯;但在類外,由於沒法訪問類的私有變量,沒法找到符號。

三十3、 初始化二維數組

1.A (*ga)[n] = new A[m][n];
...
delete []ga;
缺點:n必須是已知
優勢:調用直觀,連續儲存,程序簡潔(通過測試,析構函數能正確調用)

2. A** ga = new A*[m];
for(int i = 0; i < m; i++)
ga[i] = new A[n];
...
for(int i = 0; i < m; i++)
delete []ga[i];
delete []ga;
缺點:非連續儲存,程序煩瑣,ga爲A**類型
優勢:調用直觀,n能夠不是已知

3. A* ga = new A[m*n];
...
delete []ga;
缺點:調用不夠直觀
優勢:連續儲存,n能夠不是已知

4. vector<vector<A> > ga;
ga.resize(m); //這三行可用可不用
for(int i = 1; i < n; i++) //
ga[i].resize(n); //
...

缺點:非連續儲存,調試不夠方便,編譯速度降低,程序膨脹(實際速度差異不大)
優勢:調用直觀,自動析構與釋放內存,能夠調用stl相關函數,動態增加

5. vector<vector<A> > ga;
ga.resize(m*n);
方法3,4的結合

6. 2的改進版
A** ga = new A*[m];
ga[0] = new A[m*n];
for(int i = 1; i < m; i++)
ga[i] = ga[i-1]+n;
優勢:連續存儲,n能夠不是已知,析構方便,猜測只需delete [] ga;

三十4、 格式化輸出

#include <iomanip>
cout << setfill('*') << setw(6) << count << endl;

三十5、 隨機數

C中的隨機數:

#include <stdlib.h>
a = rand() % 100;       //生成0到100的隨機數
a = srand((unsigned) time(NULL));   //使用系統當前時間做爲種子點

C++中的隨機數:

//隨機數引擎
default_random_engine e;
for (size_t i = 0; i < 10; ++i)
    cout << e() << " ";

標準庫定義了多個隨機數引擎類,區別在於性能和隨機性質量不一樣。引擎的操做以下:

Engine e;
Engine e(s);        //使用整型值s做爲種子點
e.seed(s);          //從新設置種子爲s
e.min() e.max()     //引擎生成的最小值和最大值

爲了獲得一個指定範圍內的數,咱們使用一個分佈類型的對象:

uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e;
for (size_t i = 0; i < 10; ++i)
    cout << u(e) << " ";        //生成均勻分佈的unsigned值

3六、內存操做

#include <string.h>
memcpy(dst, src, size);
memset(src, 0, size);
int* a = (int*)malloc(size);
if (a)
    free(a);

37. c string

#include <string.h>
char* str = "hello world";
int len = strlen(str);
strcmp(str1, str2);
strcpy(str1, str2);
strncpy(str1, str2, size); //拷貝前size個字符
strcat(str1, str2);
strncat(str1, str2, size);   //鏈接前size個字符
char *strchr(const char *s,char c);         //查找字符串s中首次出現字符c的位置
strstr(string,search);              //查找字符串s中出現字符串search的位置

38. clock vs time

clock是cpu用時,time是從UTC時間開始計時,程序真正執行的時間。

// first
#include <time.h>
clock_t startTime = clock();
int elapsedTime = (clock() - startTime) / CLOCKS_PER_SEC;

// second
time_t a, b;
a = time(NULL);
//...
b = time(NULL);
float elapsed = b - a;

39. shared_ptr

gcc中的頭文件

//vs2010使用#include <memory>, 
//c++11中也直接#include<memory>,已將將它加入標準空間std中了,不須要使用tr1了

#include <tr1/memory>       


using std::tr1::shared_ptr;

void foo(){
    // c++11嵌套<>已經能夠區分,不用加空格,但爲了保證兼容性,最好仍是加上空格,由於c++11目前使用的還比較少
    shared_ptr<Matrix<int> > ptrm(new Matrix<int>(3, 4));
    ptrm->print();
}

shared_ptr<Widget> s1(new Widget());
cout << s1.use_count() << endl;    //當前有1個reference
shared_ptr<Widget> s2 = s1;
cout << s1.use_count() << endl;     //當前有2個reference
s1->print();                         //調用Widget類內函數
s1.reset();                         
cout << s2.use_cout() << endl;      //當前有1個reference
s2.reset();                         //會調用Widget的析構函數

40. C++類型轉換

  1. const_cast:常量轉很是量
  2. static_cast: 最經常使用,將intdouble
  3. dynamic_cast:安全向下轉型,轉換爲子類,效率低
  4. interpret_cast:轉換一個指針爲其它類型的指針。它也容許從一個指針轉換爲整數類型。反之亦然。(譯註:是指針具體的地址值做爲整數值?)這個操做符可以在非相關的類型之間轉換。操做結果只是簡單的從一個指針到別的指針的值的二進制拷貝。在類型之間指向的內容不作任何類型的檢查和轉換。

reinterpret_cast:

class A {};
class B {};

A * a = new A;
B * b = reinterpret_cast<B *>(a);

reinterpret_cast就像傳統的類型轉換同樣對待全部指針的類型轉換。

dynamic_cast:

class A
{
    public:
        int m_iNum;
        virtual void f(){}
};

class B:public A
{
};

class D:public A
{
};
void foo()
{
    B* pb = new B;
    pb->m_iNum = 100;
    D* pd1 = static_cast<D*>(pb); //compile error
    D* pd2 = dynamic_cast<D*>(pb); //pd2 is NULL
    delete pb;
}

在函數foo中,使用static_cast進行轉換是不被容許的,將在編譯時出錯,而使用 dynamic_cast的轉換則是容許的,結果是空指針。dynamic_cast只有當pb真的指向class D的實例時纔會有效。

41. 數值

2^16 = 65536
2^20 = 1048576

42. const

  1. const 做用域

    const char * p = "hello";       //p所指向的字符串不能變
     char const * p = "hello";       //同上效果
     char* const p = "hello";        //p的值沒法改變,不能指向它處
  2. const 重載

(1) const 修飾參數
+ 若參數是基本類型,不可構成重載
+ 若參數是自定義類型,能夠構成重載

(2) const 修飾函數
能夠構成重載,const成員函數也不能在函數中調用其餘非const 的函數

(3) const 修飾返回值
不能夠進行重載

  1. const的例外

    const函數執行bitwise檢查,函數裏不能夠改變值。若要進行改變,須要將變量聲明爲mutable,添加例外。

  2. const的調用

    對象.成員函數
          對象          成員函數         對/錯
     一、  const         const            對
     二、  const         non-const        錯 
     三、  non-const     const            對
     四、  non-const     non-const        對
    
     成員函數調用成員函數
          成員函數       成員函數         對/錯
     五、  const         const            對
     六、  const         non-const        錯
     七、  non-const     const            對
     八、  non-const     non-const        對

43. 線程安全

mutex:互斥
實現:

44. volatile

它是被設計用來修飾被不一樣線程訪問和修改的變量。若是沒有volatile,基本上會致使這樣的結果:要麼沒法編寫多線程程序,要麼編譯器失去大量優化的機會。volatile修飾的變量可能隨時發生變化,每次都要從新讀取,編譯器不要進行優化。

volatile的做用是: 做爲指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值.

45. binary_function

template <typename T>
struct com : public binary_function<Matrix<T>, Matrix<T>, bool>{
    bool operator() (const Matrix<T>& lhs, const Matrix<T>& rhs) const{
       return lhs.getD() < rhs.getD(); 
    }
};

//排序算法中調用
sort(v.begin(), v.end(), com<int>());
//或者先定義一個對象,再傳進去
com<int> in;
sort(v.begin(), v.end(), in);

46. 常量區

char str1[] = "abc";
char str2[] = "abc";
const char3[] = "abc";
const char4[] = "abc";
char *str5 = "abc";
char *str6 = "abc";
const char *str7 = "abc";
const char *str8 = "abc";

其中,str1, str2, str3, str4分別指向不一樣的地址,後面的四個指向共同的常量區。而且常量字符沒法修改,嘗試修改會出現Segment Fault

47. 進制

0x數據 十六進制
0數據 八進制
直接數據 十進制

而%d,%o,%x,%X 分別是 十進制,八進制,十六進制(小寫),十六進制(大些)的輸出格式!

48. extern "C"

C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不一樣。假設某個函數的原型爲: void foo(int x, int y);
該函數被C編譯器編譯後在庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。
C++提供了C鏈接交換指定符號extern「C」來解決名字匹配問題。

49. strcpy

(1)strcpy實現

char *strcpy(char *strDest, const char *strSrc);
{
    assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
    char *address = strDest;    // 2分
    while( (*strDest++ = * strSrc++) != ‘\0’ )  // 2分
       NULL ; 
    return address ;    // 2分
}

(2)strcpy能把strSrc的內容複製到strDest,爲何還要char * 類型的返回值?
答:爲了實現鏈式表達式。 // 2分

int length = strlen( strcpy( strDest, 「hello world」) );

50. 複雜聲明

右左法則:首先從最裏面的圓括號看起,而後往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號裏面全部的東西,就跳出圓括號。重複這個過程直到整個聲明解析完畢。

  1. 先從未定義的標識符func開始,因爲左邊是*,說明func是個指針,跳出括號,看右邊是個括號,func是個函數指針,指向的函數有int*的參數,返回值爲int.

    int (*func)(int *p);
  2. func開始,左側是*,說明是指針,跳出括號,右側是個括號,說明func是個函數指針,其函數是兩個參數,一個是int*,另外一個是以int*爲參數,int爲返回值的函數指針,外面函數返回值類型是int.

    int (*func)(int *p, int (*f)(int*));
  3. 先看func,[]的優先級大於,先看左側[],func是一個數組,再看*,這個數組存放的是指針,跳出括號,看右側是個括號,可知數組中存放的是函數指針,其函數有一個int的參數,返回值爲int.

    int (*func[5])(int *p);
  4. 先看func,其左側是個*,可知是個指針,跳出括號,左側是個數組,說明func是個指向數組的指針,右側有個 *,說明func中存放的是函數指針,其函數有個參數是int*,返回值是int。

    int (*(*func)[5])(int *p);
  5. func是個函數指針,參數是int*,返回值是指向數組的指針,指向的數組有5個int

    int (*(*func)(int *p))[5];
  6. 也有一些不符合語法規則:不返回數組;不容許函數數組,由於通常函數大小不同。

    int func(void) [5];
    
     int func[5](void);
  7. 其它

    int (*(*func)[5][6])[7][8];
    
     int (*(*(*func)(int *))[5])(int *);
    
     int (*(*func[7][8][9])(int*))[5];
  8. 對錶達式分解會更加清晰

    int (*(*func)(int *p))[5];

    能夠這樣分解:

    typedef int (*PARA)[5]; 
     typedef PARA (*func)(int *);

51. typename vs class

  1. typenameclass均可以用來在模板中聲明模板參數和類型參數。早期只有class,後來爲了區分類,提供了typename,在這一方面,typenameclass同樣,根據習慣使用。

  2. typename還有一個用途,標明內嵌類型名,這時只能使用typename

    typename vecotr::const_iterator start(v.begin());           //聲明內嵌類型const_iterator

52. C++ 三個基本特徵

封裝:隱藏實現細節,使代碼模塊化。把客觀事物封裝成抽象的類,而且類能夠把本身的數據和方法只讓可信的類或者對象操做,對不可信的進行隱藏。(代碼重用)

繼承:繼承可使用現有類的全部功能,無需從新編寫原來的類而對其進行擴展。(代碼重用)

多態:父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做,容許將子類類型的指針賦值給父類類型的指針。

53. 繼承方式

  1. 繼承方式

    public繼承: public => public, protect =>protect, private => 不可訪問
    protect繼承:public => protect, protect =>protect, private => 不可訪問
    private繼承:public => private, protect => private, private => 不可訪問

  2. 子類成員

    public成員:public => public, protect => protect, private => private
    protect成員:public => protect, protect => protect, private => private
    private成員:都不可訪問

默認繼承方式是private繼承,默認成員是private。

54. ,表達式

逗號表達式的通常形式是:表達式1,表達式2,表達式3……表達式n
逗號表達式的求解過程是:先計算表達式1的值,再計算表達式2的值,……一直計算到表達式n的值。最後整個逗號表達式的值是表達式n的值。

int x = 7, y = 2, z;
z = (x%y,x/y)  先計算x%y =1, 再計算x/y=3,而後 z=(1,3)=3

55. 棧展開

因發生異常而逐步退出複合語句和函數定義的過程,被稱爲棧展開。

棧展開過程沿着嵌套函數的調用鏈不斷查找,直到找到與異常匹配的catch子句爲止,若是沒有找到匹配的子句,調用標準庫中的terminate函數終止進程

在棧展開過程,在一些語句塊建立的對象應該被正確銷燬,調用其析構函數。

56. 異常

  1. catch子句的參數類型

    使用pass by reference方法,能夠對異常的修改起做用而且提升效率,通常將catch的參數類型定義爲引用類型。

  2. 查找匹配的catch子句

    a. 類型轉換支持的比函數參數匹配的少,不支持算術類型轉換和類類型轉換,只支持以下:
    • 很是量向常量轉型
    • 派生類向基類轉型
    • 數組被轉換成指向數組類型的指針

    b. 匹配順序
    函數匹配是best-fit進行的,選擇匹配參數最好的,而異常問題是按first fit進行,按照順序進行,因此要把子類(及特殊類型)放在通用類型的前面。

  3. 異常拋出

    異常拋出:

    Exception ex;
     throw ex;

    異常拋出時,不論catch是以什麼方式捕獲by referenceby value都須要進行復制,到catch子句上的正是這個異常的副本。

    異常從新拋出:

    catch (exception& ex)
     {
         //....
         throw;                  //從新拋出此exception,使它繼續傳播
     }   
     catch (exception& ex)
     {
         //....
         throw ex;               //傳播被捕捉的exception的一個副本
     }

57. VS2010的配置選項

  1. 若是用到dll文件,能夠添加一個後期生成事件,拷貝dll文件到Debug目錄:

    copy D:\app\vlfeat-0.9.9\bin\w32\vl.dll $(SolutionDir)$(ConfigurationName)

58. 內聯函數

  1. 在內聯函數內不要使用循環語句和開關語句,這樣就達不到內聯的效果了。
  2. 關鍵字inline必須與函數定義體放在一塊兒才能使函數成爲內聯,僅將inline 放在函數聲明前面不起任何做用

59. memset

memset的用法以下:

const int NUM = 100;
int* src = new int[NUM];
memset(src, 0, sizeof(int)*NUM)

memset是按字節賦值的,不能將int賦1,若是賦1,實際結果將是0x01010101,與咱們的預期不一樣。

60. virtual 析構函數

當子類對象經由父類的指針刪除時,若是是non-virtual函數,將不會調用子類的析構函數,也就是說,對象的子類成分有可能沒被銷燬。

61. ifstream

  1. good() vs eof()

    good()表示文件流是否正常,eof表示文件流是否到結束了。good() 等價於 !(bad() || eof() || failed())

  2. ifstream in(string)

    有些編譯器不支持以string做爲參數,爲了兼容性,最後仍是轉爲c串。

62. strtok

char buf[]=」Golden Global View」;
char* token = strtok( buf, " ");
while( token != NULL )
{
    printf( 」%s 「, token );
    token = strtok( NULL, 」 「);
}

63. ::雙冒號

//雙冒號也經常用於在類變量內部做爲當前域的元素進行表示,好比:    
int CA::add(int a)    
{    
    return a + ::ca_var;    
}
//表示當前類實例中的變量ca_var。
//若是當前哉沒有,則向上一級域查找,直到找到全局域

64. 區別位運算與關係運算

位運算只有一個字符:&, |, ^
關係運算有兩個字符:&&, ||

65. 靜態分配,動態分配

內存的靜態分配和動態分配的區別主要是兩個:

  1. 一是時間不一樣。靜態分配發生在程序編譯和鏈接的時候。動態分配則發生在程序調入和執行的時候。
  2. 二是空間不一樣。堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,好比局部變量的分配。動態分配由函數malloc進行分配。不過棧的動態分配和堆不一樣,他的動態分配是由編譯器進行釋放,無需咱們手工實現。

堆和棧均可以動態分配,但堆必須動態分配,不能靜態分配。

66. 筆試題

  1. 字符串大小

    char dog[] = "wang\0miao";
     sizeof(dog)     //大小爲10
     strlen(dog)     //大小爲4
  2. 系統位數對類型大小的影響

    聯合體,結構體的總大小爲結構體最寬基本類型成員大小的整數倍。

    32位: int 4; 指針 4;
     64位: int 4; 指針 8;

    64位機器:

    /*******size of types on centos_x32*****************/
     size of char:1
     size of int:4
     size of long :4
     size of float :4
     size of long long:8
     size of double:8
     size of long double:12
     size of char * :4
     /*******size of types on centos_x64*****************/
     size of char:1
     size of int:4
     size of long :8
     size of float :4
     size of long long:8
     size of double:8
     size of long double:16
     size of char * :8
    
              32位        64位 
     char      1           1 
     int       4           大多數4,少數8 
     long      4           8 
     float     4           4 
     double    8           8 
     指針       4           8
  3. 字符串初始化方法

    char a[] = "ABC";          //大小爲4,最後有\0
     char b[] = {"A", "B", "C"};  //大小爲3, 字符數組
  4. 數組指針和指針數組

    int* a[10];        //指針數組
     int (*b)[10];      //數組指針,指向有10個char組成的數組的指針
     sizeof(a);         //sizeof(int*) * 10 = 4 * 10 = 40(32位機器)
     sizeof(b);         //sizeof(指針)=4 (32位機器)
  5. 位域

    C語言又提供了一種數據結構,稱爲「位域」或「位段」。所謂「位域」是把一個字節中的二進位劃分爲幾個不一樣的區域, 並說明每一個區域的位數。每一個域有一個域名,容許在程序中按域名進行操做。 這樣就能夠把幾個不一樣的對象用一個字節的二進制位域來表示。

    struct A{
         int x : 3;
         int y : 4;
         int z : 5;     //佔用int中的5位 
     };
    
     sizeof(A);    //大小爲4字節,由於只佔用了一個int
  6. 純虛函數

    純虛函數能夠有函數體,但沒有什麼意義。

  7. 類型轉換

    32位機上根據下面的代碼,問哪些說法是正確的?()
     signed char a = 0xe0;
     unsigned int b = a;
     unsigned char c = a;
    
     A. a>0 && c>0 爲真
     B. a == c 爲真
     C. b 的十六進制表示是:0xffffffe0
     D. 上面都不對
    1. 同等位數的類型之間的賦值表達式不會改變其在內存之中的表現形式,所以經過 unsigned char c = a;語句,c的位存儲形式仍是0xe0
    2. B選項中的"==" 左右兩邊須要進行Integer Promotion,將其提高爲int類型。
    3. 提高的方法:若是原始類型爲unsigned ,進行零擴展;若是原始類型爲signed,進行符號位擴展,注意是原來的類型,不是提高後的類型。
    4. 若是unsigned和int進行計算,則會將int轉化成unsigned以後進行計算,但不會改變內存的表現形式。

    好比下面的代碼,返回的結果爲真,由於a和b進行轉型時,不發動內存內容,而==比較都會轉換爲int,因此最後結果相同。

    signed int a = 0xe0000000
     unsigned int b = a;
     cout<< (b == a) <<endl;
  8. ASCII表

    Alt text

    0x20:  space
     0x30:  0
     0x41:  A
     0x61:  a
  9. 彙編

    AT&T風格的彙編代碼。

    int main()
     {
         char data = 'a';
         char* p = &data;
         *p++;
         (*p)++;
         return 0;
     }
    
     //使用objdump -S main 輸出mian部分的彙編代碼
    
     0000000000400746 <main>:
       400746:   55                      push   %rbp
       400747:   48 89 e5                mov    %rsp,%rbp
       40074a:   48 83 ec 20             sub    $0x20,%rsp
       40074e:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
       400755:   00 00 
       400757:   48 89 45 f8             mov    %rax,-0x8(%rbp)
       40075b:   31 c0                   xor    %eax,%eax
    
     l1:    char data = 'a';    
       40075d:   c6 45 ef 61             movb   $0x61,-0x11(%rbp)   //rbp[-0x11] = 0x61
    
     l2:    char* p = &data;
       400761:   48 8d 45 ef             lea    -0x11(%rbp),%rax
       400765:   48 89 45 f0             mov    %rax,-0x10(%rbp)
    
     l3:     *p++;
       400769:   48 83 45 f0 01          addq   $0x1,-0x10(%rbp)
    
     l4:    (*p)++;
       40076e:   48 8b 45 f0             mov    -0x10(%rbp),%rax
       400772:   0f b6 00                movzbl (%rax),%eax
       400775:   83 c0 01                add    $0x1,%eax
       400778:   89 c2                   mov    %eax,%edx
       40077a:   48 8b 45 f0             mov    -0x10(%rbp),%rax
    
       40077e:   88 10                   mov    %dl,(%rax)
       400780:   b8 00 00 00 00          mov    $0x0,%eax
       400785:   48 8b 4d f8             mov    -0x8(%rbp),%rcx
       400789:   64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
       400790:   00 00 
       400792:   74 05                   je     400799 <main+0x53>
       400794:   e8 a7 fe ff ff          callq  400640 <__stack_chk_fail@plt>
       400799:   c9                      leaveq 
       40079a:   c3                      retq
  10. 數組名

    數組名是常量,沒法給值賦值。

    char str[100];
    str++;    //出錯,str是常量沒法修改
  11. 類方法是指類中被static修飾的方法,無this指針。

相關文章
相關標籤/搜索