總結 1

    多態性是允許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。

    多態的作用:繼承是子類使用父類的方法,而多態則是父類使用子類的方法。這是一句大白話,多態從用法上就是要用父類(確切的說是父類的對象名)去調用子類的方法

    多態性使得能夠利用同一類(基類)類型的指針來引用不同類的對象,以及根據所引用對象的不同,以不同的方式執行相同的操作。把不同的子類對象都當作父類來看,可以屏蔽不同子類對象之間的差異,寫出通用的代碼,做出通用的編程,以適應需求的不斷變化。賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作(也就是可以調用子對象中對父對象的相關函數的改進方法)。

    1. 派生的對象可以賦給基類的對象
A a;
B b;
a = b;
2. 派生的對象可以初始化基類的引用
B b;
A &a = b;
3. 派生的對象的地址可以賦給指向基類的指針
B b;
A *a = &b;

A *a = new B();
由上述對象賦值兼容規則可知,一個基類的對象可兼容派生類的對象,一個基類的指針可指向派生類的對象,一個基類的引用可引用派生類的對象,於是對於通過基類的對象指針(或引用)對成員函數的調用,編譯時無法確定對象的類,而只是在運行時才能確定並由此確定調用哪個類中的成員函數


這就引出虛函數的原理了:當編譯器遇到virtual後,會爲所在的類構造一個表和一個指針,那個表叫做vtbl,每個類都有自己的vtbl,vtbl的作用就是保存自己類中虛函數的地址,我們可以把vtbl形象地看成一個數組,這個數組的每個元素存放的就是虛函數的地址.指針叫做vptr,指向那個表。而這個指針保存在相應的對象當中,也就是說只有創建了對象以後才能找到相應虛函數的地址。

純虛函數:
虛函數的作用是爲了實現對基類與派生類中的虛函數成員的遲後聯編,而純虛函數是表明不具體實現的虛函數成員,即純虛函數無實現代碼。其作用僅僅是爲其派生類提過一個統一的構架,具體實現在派生類中給出。
一個函數聲明爲純虛後,純虛函數的意思是:我是一個抽象類!不要把我實例化!純虛函數用來規範派生類的行爲,實際上就是所謂的“接口”。它告訴使用者,我的派生類都會有這個函數。


??抽象基類 析構函數不爲虛函數 會怎樣

??


9. 什麼是 “ 引用 ” ?申明和使用 “ 引用 ” 要注意哪些問題?
答:引用就是某個目標變量的 “ 別名 ”(alias) ,對應用的操作與對變量直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。引用聲明完畢後,相當於目標變量名有兩個名稱,即該目標原名稱和引用名,不能再把該引用名作爲其他變量名的別名。聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數據類型,因此引用本身不佔存儲單元,系統也不給引用分配存儲單元。不能建立數組的引用。
——————————————————————————————–
10. 將 “ 引用 ” 作爲函數參數有哪些特點?
( 1 )傳遞引用給函數與傳遞指針的效果是一樣的。這時,被調函數的形參就成爲原來主調函數中的實參變量或對象的一個別名來使用,所以在被調函數中對形參變量的操作就是對其相應的目標對象(在主調函數中)的操作。

( 2 )使用引用傳遞函數的參數,在內存中並沒有產生實參的副本,它是直接對實參操作;而使用一般變量傳遞函數的參數,當發生函數調用時,需要給形參分配存儲單元,形參變量是實參變量的副本;如果傳遞的是對象,還將調用拷貝構造函數。因此,當參數傳遞的數據較大時,用引用比用一般變量傳遞參數的效率和所佔空間都好。

( 3 )使用指針作爲函數的參數雖然也能達到與使用引用的效果,但是,在被調函數中同樣要給形參分配存儲單元,且需要重複使用 “* 指針變量名 ” 的形式進行運算,這很容易產生錯誤且程序的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的地址作爲實參。而引用更容易使用,更清晰。

 在什麼時候需要使用 “ 常引用 ” ?
如果既要利用引用提高程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。常引用聲明方式: const 類型標識符 & 引用名 = 目標變量名;


12. 將 “ 引用 ” 作爲函數返回值類型的格式、好處和需要遵守的規則 ?
格式:類型標識符 & 函數名(形參列表及類型說明) { // 函數體 }

好處:在內存中不產生被返回值的副本;(注意:正是因爲這點原因,所以返回一個局部變量的引用是不可取的。因爲隨着該局部變量生存期的結束,相應的引用也會失效,產生 runtime error! 注意事項:

( 1 )不能返回局部變量的引用。這條可以參照 Effective C++[1] 的 Item 31 。主要原因是局部變量會在函數返回後被銷燬,因此被返回的引用就成爲了 ” 無所指 ” 的引用,程序會進入未知狀態。

( 2 )不能返回函數內部 new 分配的內存的引用。這條可以參照 Effective C++[1] 的 Item 31 。雖然不存在局部變量的被動銷燬問題,可對於這種情況(返回函數內部 new 分配內存的引用),又面臨其它尷尬局面。例如,被函數返回的引用只是作爲一個臨時變量出現,而沒有被賦予一個實際的變量,那麼這個引用所指向的空間(由 new 分配)就無法釋放,造成 memory leak 。

( 3 )可以返回類成員的引用,但最好是 const 。這條原則可以參照 Effective C++[1] 的 Item 30 。主要原因是當對象的屬性是與某種業務規則( business rule )相關聯的時候,其賦值常常與某些其它屬性或者對象的狀態有關,因此有必要將賦值操作封裝在一個業務規則當中。如果其它對象可以獲得該屬性的非常量引用(或指針),那麼對該屬性的單純賦值就會破壞業務規則的完整性。

( 4 )流操作符重載返回值申明爲 “ 引用 ” 的作用:

流操作符 << 和 >> ,這兩個操作符常常希望被連續使用,例如: cout << “hello” << endl;  因此這兩個操作符的返回值應該是一個仍然支持這兩個操作符的流引用。可選的其它方案包括:返回一個流對象和返回一個流對象指針。但是對於返回一個流對象,程序必須重新(拷貝)構造一個新的流對象,也就是說,連續的兩個 << 操作符實際上是針對不同對象的!這無法讓人接受。對於返回一個流指針則不能連續使用 << 操作符。因此,返回一個流對象引用是惟一選擇。這個唯一選擇很關鍵,它說明了引用的重要性以及無可替代性,也許這就是 C++ 語言中引入引用這個概念的原因吧。 賦值操作符 = 。這個操作符象流操作符一樣,是可以連續使用的,例如: x = j = 10; 或者 (x=10)=100; 賦值操作符的返回值必須是一個左值,以便可以被繼續賦值。因此引用成了這個操作符的惟一返回值選擇。



114. 交換兩個數的宏定義

交換兩個參數值的 宏定義 爲: . #define SWAP (a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);
——————————————————————————————–
115.Itearator 各指針的區別

遊標和指針

我說過遊標是指針,但不僅僅是指針。遊標和指針很像,功能很像指針,但是實際上,遊標是通過重載一元的 ”*” 和 ”->” 來從容器中間接地返回一個值。將這些值存儲在容器中並不是一個好主意,因爲每當一個新值添加到容器中或者有一個值從容器中刪除,這些值就會失效。在某種程度上,遊標可以看作是句柄( handle )。通常情況下游標( iterator )的類型可以有所變化,這樣容器也會有幾種不同方式的轉變:

iterator—— 對於除了 vector 以外的其他任何容器,你可以通過這種遊標在一次操作中在容器中朝向前的方向走一步。這意味着對於這種遊標你只能使用 “++” 操作符。而不能使用 “–” 或 “+=” 操作符。而對於 vector 這一種容器,你可以使用 “+=” 、 “—” 、 “++” 、 “-=” 中的任何一種操作符和 “” 、 “>=” 、 “==” 、 “!=” 等比較運算符。
——————————————————————————————–
116. C++ 中的 class 和 struct 的區別
從語法上,在 C++ 中(只討論 C++ 中)。 class 和 struct 做類型定義時只有兩點區別:
(一)默認繼承權限。如果不明確指定,來自 class 的繼承按照 private 繼承處理,來自 struct 的繼承按照 public 繼承處理;
(二)成員的默認訪問權限。 class 的成員默認是 private 權限, struct 默認是 public 權限。
除了這兩點, class 和 struct 基本就是一個東西。語法上沒有任何其它區別。

不能因爲學過 C 就總覺得連 C++ 中 struct 和 class 都區別很大,下面列舉的說明可能比較無聊,因爲 struct 和 class 本來就是基本一樣的東西,無需多說。但這些說明可能有助於澄清一些常見的關於 struct 和 class 的錯誤認識:
( 1 )都可以有成員函數;包括各類構造函數,析構函數,重載的運算符,友元類,友元結構,友元函數,虛函數,純虛函數,靜態函數;
( 2 )都可以有一大堆 public/private/protected 修飾符在裏邊;
( 3 )雖然這種風格不再被提倡,但語法上二者都可以使用大括號的方式初始化:

A a = {1, 2, 3}; 不管 A 是個 struct 還是個 class ,前提是這個類 / 結構足夠簡單,比如所有的成員都是 public 的,所有的成員都是簡單類型,沒有顯式聲明的構造函數。
( 4 )都可以進行復雜的繼承甚至多重繼承,一個 struct 可以繼承自一個 class ,反之亦可;一個 struct 可以同時繼承 5 個 class 和 5 個 struct ,雖然這樣做不太好。
( 5 )如果說 class 的設計需要注意 OO 的原則和風格,那麼沒任何理由說設計 struct 就不需要注意。
( 6 )再次說明,以上所有說法都是指在 C++ 語言中,至於在 C 裏的情況, C 裏是根本沒有 “class” ,而 C 的 struct 從根本上也只是個包裝數據的語法機制。
—————————————————————

最後,作爲語言的兩個關鍵字,除去定義類型時有上述區別之外,另外還有一點點: “class” 這個關鍵字還用於定義模板參數,就像 “typename” 。但關鍵字 “struct” 不用於定義模板參數。

在模版中,類型參數前面可以使用class或typename,如果使用struct,則含義不同,struct後面跟的是“non-type template parameter”,而class或typename後面跟的是類型參數。
template
void f(X x)
{
}
//出錯信息:d:codecpptestcpptestcpptest.cpp(33) : error C2065: ‘X’ : undeclared identifier

關於使用大括號初始化

class 和 struct 如果定義了構造函數的話,都不能用大括號進行初始化

如果沒有定義構造函數, struct 可以用大括號初始化。

如果沒有定義構造函數,且所有成員變量全是 public 的話,可以用大括號初始化。

關於默認訪問權限

class 中默認的成員訪問權限是 private 的,而 struct 中則是 public 的。

關於繼承方式

class 繼承默認是 private 繼承,而 struct 繼承默認是 public 繼承。

關於模版

在模版中,類型參數前面可以使用 class 或 typename ,如果使用 struct ,則含義不同, struct 後面跟的是 “non-type template parameter” ,而 class 或 typename 後面跟的是類型參數。

class 中有個默認的 this 指針, struct 沒有
不同點:構造函數,析構函數 this 指針
——————————————————————————————–
117. 有關重載函數

返回值類型不同構不成重載
參數參數順序不同能構成重載

c++ 函數同名不同返回值不算重載!函數重載是忽略返回值類型的。

成員函數被重載的特徵有:
1) 相同的範圍(在同一個類中);
2) 函數名字相同;
3) 參數不同;
4) virtual 關鍵字可有可無。

5) 成員函數中 有無 const ( 函數後面 ) 也可判斷是否重載

1. 下面這段代碼的輸出是多少(在32位機上).
char *p;
char *q[20];
char *m[20][20];
int (*n)[10];
struct MyStruct
{
char dda;
double dda1;
int type ;
};
MyStruct k;
printf(“%d %d %d %d”,sizeof(p),sizeof(q),sizeof(m),sizeof(n),sizeof(k));
答案:4,80,1600,4,24


用遞歸方式,非遞歸方式寫函數將一個字符串反轉.
函數原型如下:char *reverse(char *str);
答:非遞歸方式:

char *reverse(char *str)
{
    if(str!=NULL)
    {
        int length = strlen(str);
        for(int i=0;i<length/2;++i)
        {
            char temp=str;
            str= str[length-1];
            str[length-1] =temp;
         }
    }
    printf(“%s\n”,str);
}

4.strcpy函數和memcpy函數有什麼區別?它們各自使用時應該注意什麼問題?

5.寫一個函數將一個鏈表逆序.

6一個單鏈表,不知道長度,寫一個函數快速找到中間節點的位置.
答:

typedef struct linknode
{
    int a;
    struct linknode *next;
}linknode;

linknode *findmiddle(linknode *head)
{
    linknode *fast,*low,*p;
    if(head==NULL)
    {
        return NULL;
    }
    fast=low=head;
    while(!(p=fast->next)&&!p->next)
    {
        low=low->next;
        fast=p->next;
    }
    return low;
}

7 寫一個函數找出一個單向鏈表的倒數第n個節點的指針.(把能想到的最好算法寫出).
答:兩個指針,第一個先於第二個n個位置。

LinkNode IsLoopList(LinkNode *head,int n)
{
    LinkNode *p1,*p2,*p3;
    P1 = head;
    While(n–>=0)
    {
        P3= p2->next;
        P2 = p3;
    }
    While(p3->next!=NULL)
    {
        P3 = p3->next;
        P1 = p1->next;
    }
    Return p1; //當p3到達結尾時,後於p3n個位置的就是倒數第n個節點。
}

10.判斷鏈表是否循環;
答:

bool IsLoopList(LinkNode *head)
{
    LinkNode *p1= head,*p2= head;
    if(head ->next==NULL)//只包含頭節點,且頭節點的next爲NULL說明肯定不是循環鏈表
    return false;
    do{
        p1=p1->next; //步長爲1
        p2=p2->next->next; //步長爲2
    }while(p2 && p2->next && p1!=p2);
    if(p1==p2)
    return true;
    else
    return false;
}




strcpy,strcat,鏈表逆序,排序,字符串匹配查找,檢測單向鏈表上的環等

virtual,const,static,extern “C”,虛函數表,構造、析構函數可否爲virtual,純虛函數,虛擬繼承

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章