程序設計基本概念
1 一個(gè)小程序,輸出結果
int x = 2, y,z;
x *= (y = z= 5);//等價(jià)于x= x * y
cout<< x << endl;//輸出為10
z = 3;
int t = (x ==(y = z));
cout<< x << endl;//輸出為10
cout<< t << endl;//輸出為0
x = (y ==z);
cout<< x << endl;//輸出為1
x = y &z;
cout<< x << endl;//輸出為3
x = y&& z;
cout<< x << endl;//輸出為1
y = 4;
x = (y | z);
cout<< x << endl;//輸出為7
x = (y ||z);
cout<< x << endl;//輸出為1
2 int i = 1, j =2;
int k = i+++j;//等價(jià)于(i++)+ j
cout<< k << endl;//輸出為3
微軟公司的最新C++編譯器2005解讀C++源程序時(shí)如果需要“斷句”,則規則是每次都會(huì )先找到包含盡量多的字符的一個(gè)語(yǔ)素后再在其后斷開(kāi)。比如對于欠揍人寫(xiě)的欠揍語(yǔ)句
i+++j (本身可能解釋成i++ +j和i+ ++j兩種語(yǔ)句 )
但是按照以上原則:則前面語(yǔ)素必須擁有最多的字符:于是只能解釋成 i+++j 等效為 i++ + j
3 x = x + 1, x +=1, x++哪個(gè)效率最高?為什么?
X++ > x +=1 >x = x+1
x = x + 1: (1)讀取右x的地址(2)x+1(3)讀取左x的地址(編譯器病不認為左右x的地址相同)(4)講右邊的只給左邊的x
x+=1:(1)讀取右邊x的地址(2)x+1(3)講得到的值給x(因為x的地址已經(jīng)讀出)
x++:(1)讀取右x的地址(2)x自增1
4 輸出
#define product(x)(x * x)
int i = 3, j,k;
j =product(i++);
k =product(++i);
cout<< j << " " << k<< endl;//輸出為9和49
即使定義為#defineproduct(x) ((x) * (x))得到的結果還是一樣
5 類(lèi)型轉換
char foo(void)
{
unsigned int a = 6;
int b = -20;
char c;
(a + b >6) ? (c = 1) : (c = 0);
return c;// 此時(shí)c=1
}
Unsignedint類(lèi)型的數據與int類(lèi)型的數據相運算后,自動(dòng)轉化為unsignedint類(lèi)型,因此a+b
的結果不是-14,而是一個(gè)unsignedint類(lèi)型的數4294967382,當表達式中存在有符號類(lèi)型和
無(wú)符號類(lèi)型時(shí),所有的操作數都自動(dòng)轉換為無(wú)符號類(lèi)型
1在混合類(lèi)型的算數表達式中
在這種情況下最寬的數據類(lèi)型稱(chēng)為目標轉換類(lèi)型,這也被稱(chēng)為算數轉換
int ival = 3;
double dval =3.141592;
cout<< ival + dval << endl;//輸出3.14159,這里int被提升為了double類(lèi)型
2 用一種類(lèi)型的表達式賦值為另外一種類(lèi)型的對象
這種情況下目標轉換類(lèi)型是被賦值對象的類(lèi)型
int *p = 0;
int t = dval;
6 a,b中較大的值,不用if,?:switch語(yǔ)句實(shí)現
int a, b;
cin >>a >> b;
int max = (a +b + abs(a - b)) / 2;
cout<< max << endl;
7 a,b進(jìn)行交換
方案一:
a = a + b;
b = a - b;
a = a - b;
方案二:
a = a ^ b;
b = a ^ b;
a = a ^ b;(已經(jīng)證明是對的)
方案一對大數據無(wú)能為力,因為a + b會(huì )超界
一點(diǎn)解釋?zhuān)?/span>
a = a ^ b;
b = a ^ b = a ^ b ^ b = a ^ 0 = a;
a= a ^ b= a ^ b ^ a = 0 ^ b = b;
異或
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
1 ^ 1 = 0
C++按位異或運算符^
參與運算的兩個(gè)值,如果兩個(gè)相應位相同,則結果為0,否則為1。即:0^0=0, 1^0=1, 0^1=1, 1^1=0
例如:10100001^00010001=10110000
0^0=0,0^1=1 0異或任何數=任何數
1^0=1,1^1=0 1異或任何數-任何數取反
任何數異或自己=把自己置0
(1)按位異或可以用來(lái)使某些特定的位翻轉,如對數10100001的第2位和第3位翻轉,可以將數與00000110進(jìn)行按位異或運算。
10100001^00000110=10100111 //1010 0001 ^ 0x06 = 1010 0001 ^ 6
(2)通過(guò)按位異或運算,可以實(shí)現兩個(gè)值的交換,而不必使用臨時(shí)變量。例如交換兩個(gè)整數a,b的值,可通過(guò)下列語(yǔ)句實(shí)現:
a=10100001,b=00000110
a=a^b; //a=10100111
b=b^a; //b=10100001
a=a^b; //a=00000110
(3)異或運算符的特點(diǎn)是:數a兩次異或同一個(gè)數b(a=a^b^b)仍然為原值a.
8 在c++程序中調用被c編譯器編譯后的函數,為什么要加上extern “C”?
C++支持函數重載,c語(yǔ)言不支持函數重載,函數被c++編譯后在庫中的名字與c語(yǔ)言不
同,假設某個(gè)函數的原型為:void foo(int x,int y)。該函數被c編譯器編譯后在庫中的
名字為foo_,而c++編譯器則會(huì )產(chǎn)生像_foo_int_int之類(lèi)的名字
C++提供了c鏈接交換指定符號extern “C”解決名字匹配問(wèn)題
9 #include<filename.h>與#include “filename.h”有什么區別?
對于尖括號來(lái)說(shuō),編譯器從標準庫路徑開(kāi)始搜索filename.h
對于圓括號來(lái)說(shuō),編譯器先從用戶(hù)的定義的文件開(kāi)始查找,找不到再在標準庫中進(jìn)行查找
10 如何判斷一段程序是由c編譯器還是由c++編譯器編譯的?
C++編譯時(shí)定義了_cplusplus
C編譯時(shí)定義了_STDC_
#ifdef _cplusplus
cout << "hello,cpp!";
#endif
#ifdef _STDC_
printf("hello,c!\n");
#endif
這兩者實(shí)際上是可以共存在,很多編譯器上都是兩者共存
11 main主函數執行完畢后,是否可能會(huì )再只執行一段代碼?給出說(shuō)明
如果需要加入一段在main退出后執行的代碼,可以使用atexit()函數注冊一個(gè)函數
atexit(fn1);//輸出next
atexit(fn2);//輸出executed
atexit(fn3);//輸出is
atexit(fn4);//輸出This
cout<< "This is executed first"<< endl;
最終結果為:This is executed first This is executed next
注意:atexit()注冊的函數類(lèi)型應為不接受任何參數的void函數,exit調用這些注
冊函數的順序與它們 登記時(shí)候的順序相反
預處理,Const與sizeof
1 代碼輸出
#define SQR(x) (x *x)
int a, b = 3;
a = SQR(b +2);
cout<< a << endl;//輸出為11
如果把定義為#define SQR(x) ((x)* (x))輸出結果變?yōu)?/span>25
2 宏定義
寫(xiě)一個(gè)標準的“宏”MIN,這個(gè)宏輸入兩個(gè)參數并返回較小的一個(gè)
#define MIN(x, y)((x) <= (y) ? (x) : (y))
注意:#define MIN(x, y)(x) <= (y) ? (x) : (y)這樣就是錯誤的
3 const與#define相比有什么不同?
(1) const常量有數據類(lèi)型,而宏常量沒(méi)有數據類(lèi)型,編譯器可以對前者進(jìn)行類(lèi)型安全檢測,而對后者只進(jìn)行字符替換,沒(méi)有類(lèi)型男權檢查,并且在字符替換中可能uixiang產(chǎn)生意料不到的錯誤(邊際效應)
(2) 有些集成化的調試工具可以對const常量進(jìn)行調試,但是不能對宏常量進(jìn)行調試,在c++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量
4 數據對齊原則
struct Node
{
long a1;
short a2;
};
class person
{
private:
long a1;
short a2;
};
cout << sizeof(long) << endl;//輸出為4
cout << sizeof(int) << endl;//輸出為4
cout << sizeof(short) << endl;//輸出為2
Node t;
cout << sizeof(t)<< endl;//輸出為8
person per;
cout<< sizeof(per) << endl;//輸出為8
在默認情況下,為了方便對結構體內元素的訪(fǎng)問(wèn)和管理,當結構體內的元素的長(cháng)度都小于處
理器的位數的時(shí)候,便以結構體里面最長(cháng)的數據元素為對齊單元,也就是說(shuō),結構體的長(cháng)度
一定是最長(cháng)的數據元素的整數倍,如果結構體內存在長(cháng)度大于處理器位數的元素,那么就以
處理器的位數為對齊單位,但是結構體內類(lèi)型相同的聯(lián)素元素將在連續的空間內,和數組一
樣
class A
{
};
class A2
{
char d, e;
};
cout << sizeof(A)<< endl;//輸出為1
cout<< sizeof(A2) << endl;//輸出為2
注意:對于一個(gè)類(lèi)而言,即便是它為空類(lèi),編譯器仍然要給它分配一個(gè)空間,即使它什么也
沒(méi)有
class A3
{
static int ch;
};
cout<< sizeof(A3) << endl;//輸出為1
因為靜態(tài)變量是存放在全局數據區的,而sizeof計算棧中分配的大小,是不會(huì )計算在內的
class T1
{
};
class T2: public virtual T1
{
};
cout << sizeof(T1)<< endl;//輸出為1
cout << sizeof(T2)<< endl;//輸出為4
空類(lèi)所占空間為1,單一繼承的空類(lèi)也為1,多重繼承的空類(lèi)空間還是1,但是虛繼承涉及
到虛表(虛指針),所以sizeof(T2)的大小為4
對于一個(gè)空類(lèi)而言,事實(shí)上它有一個(gè)隱晦的一字節,那是被編譯器安插進(jìn)去的一個(gè)char,這
使得這個(gè)class的兩個(gè)對象得以在內存中配置獨一無(wú)二的地址
5 內聯(lián)函數和宏的主要差別是什么?
內聯(lián)函數和普通函數相比可以加快程序運行速度,因為不需要中斷調用,
在編譯的時(shí)候 內聯(lián)函數可以直接被鑲嵌到目標代碼中,
而宏只是一個(gè)簡(jiǎn)單的替換,內聯(lián)函數要做參數類(lèi)型檢查,這是內聯(lián)函數跟宏相比的優(yōu)勢
指針與引用
1 關(guān)于this指針
(1) this只能在成員函數中使用
全局函數,靜態(tài)函數都不能使用this,實(shí)際上,成員函數默認的第一個(gè)參數是T * const this,由此可見(jiàn),this在成員函數的開(kāi)始前構造,在成員的結束后清除,這個(gè)聲明周期同任何一個(gè)函數的參數是一樣的,沒(méi)有任何區別,我們會(huì )發(fā)現在調用的形式上與靜態(tài)調用沒(méi)有什么區別,但區別還是有的,iqi通常會(huì )對this指針做一些優(yōu)化,因此,this指針的傳遞效率比較高---如VC通常是通過(guò)ecx寄存器傳遞this參數的
(2) this指針存放在何處?
This指針會(huì )因編譯器不同而有不同的存放位置,可能是棧,也可能是寄存器,甚至是全局變量,在匯編級別里面,一個(gè)值只會(huì )以3種形式出現:立即數,寄存器值和內存變量值,不是存放在寄存器就是存放在內存中,它們并不是和高級語(yǔ)言變量對應的
(3) 類(lèi)在實(shí)例化時(shí)只分配類(lèi)中的變量空間,并沒(méi)有為函數分配空間
(4) 每個(gè)類(lèi)編譯后,是否創(chuàng )建一個(gè)類(lèi)中函數表保存函數指針以便用來(lái)調用函數?
普通的類(lèi)函數(不論是長(cháng)遠函數還是靜態(tài)函數)都不會(huì )創(chuàng )建一個(gè)函數表來(lái)保存函數指針,只有虛函數才會(huì )被放到函數表中
但是,即使是虛函數,如果編譯器能明確知道調用的是哪個(gè)函數,編譯器就不會(huì )通過(guò)函數表中的指針來(lái)間接調用么人是會(huì )直接調用該函數
2 一個(gè)小程序
void swapxy(char *a, char *b)
{
int x = *a;
int y = *b;
x = x + y;
y = x - y;
x = x - y;
*a = x;
*b = y;
return;
}
char a = 'a',b = 'b';
char &x = a,&y = b;
cout <<a << " " << b <<endl;
swapxy(x, y);
cout << a << " " << b << endl;
出現的錯誤:

注意:盡管是引用,但是編譯器還是把它當作了char型,其實(shí)本來(lái)就是char型
3 錯誤的代碼
int *ptr;
ptr = (int *)0x8000;
*ptr = 7;
這樣做會(huì )導致運行時(shí)錯誤,因為這種做法會(huì )給一個(gè)指針?lè )峙湟粋€(gè)隨意的地址,這是非常危險
的,不管這個(gè)指針有沒(méi)有被使用過(guò),這樣做都是不允許的,會(huì )報寫(xiě)入沖突
4 空指針和迷途指針的區別是什么?
當delete一個(gè)指針的時(shí),實(shí)際上僅僅是讓編譯器釋放內存,當時(shí)指針本身依然存在,這時(shí)
它就是一個(gè)迷途指針
當使用下列語(yǔ)句時(shí),可以把迷途指針改為一個(gè)空指針
MyPtr = 0;
通常在刪除一個(gè)指針后又把它刪除一次,程序會(huì )變得非常不穩定,任何情況都可能發(fā)生,
但是如果只是刪除了一個(gè)空指針,則什么事都不會(huì )發(fā)生,這樣做非常安全
使用迷途指針或空指針是非法的,而且有可能造成程序崩潰,如果指針是空指針們盡管同
樣是崩潰嗎但是它同迷途指針造成的崩潰相比是一種可以預料的崩潰,這樣調試起來(lái)會(huì )方
便很多
5 c++中有了malloc/free,為什么還要new/delete?
Malloc和free是c++/c語(yǔ)言的標準庫函數,new與delete是c++的運算符,他們都可以用
于申請動(dòng)態(tài)內存和釋放內存
對于非內部數據類(lèi)型的對象而言,光用malloc與free無(wú)法滿(mǎn)足動(dòng)態(tài)對象的要求,對象在創(chuàng )
建的同時(shí)要自動(dòng)執行構造函數,對象在消亡之前要自動(dòng)執行析構函數,由于malloc和free
是庫函數而不是運算符,不再編譯器控制權限之內,不能夠把執行構造函數和析構函數的任
務(wù)強加于malloc與free
因此,c++語(yǔ)言需要一個(gè)能完成動(dòng)態(tài)內存分配和初始化工作的運算符new,以及一個(gè)能完成
清理與釋放內存工作的運算符delete,new與delete不是庫函數,而是運算符
添加一點(diǎn):
malloc/free 和new/delete的區別:
1) new/delete是保留字,不需要頭文件支持. malloc/free需要頭文件庫函數支持. 使用
malloc/free需要包含#include<cstdlib> 或<stdlib>.
2) new 建立的是一個(gè)對象,new會(huì )根據對象計算大小,直接返回對象的指針,當使用完畢后
調用delete來(lái)釋放,但malloc分配的是一塊內存,需要用戶(hù)制定所要分配內存的大小,
而且返回的均為void的指針,使用時(shí)需要相應的強制類(lèi)型轉化,使用結束后調用free來(lái)
釋放內存.
2) new/delete的使用除了分配內存和釋放,還調用了類(lèi)型的構造函數和析構函數,而
malloc/free只是簡(jiǎn)單的分配和釋放內存。
6 句柄和指針的區別和聯(lián)系是什么?
句柄和指針其實(shí)是兩個(gè)截然不同的概念,Windows系統用句柄標記系統資源,用句柄隱藏
系統的信息,只要知道有這個(gè)東西存在然后去調研那個(gè)就行了,它是一個(gè)32bit的uint,
只恨則標記某個(gè)物理內存地址,兩者是不同的概念
STL模版與容器
1 STL是跨平臺的,一個(gè)類(lèi)的模版叫做泛型類(lèi),一個(gè)函數的模版也自然叫做泛型函數
游標(iterator)
2 介紹一下STL和包容器
C++的一個(gè)新特性就是采用了標準模版庫,所有主要編程器銷(xiāo)售商都把裱糊在內模版庫作
為編譯器的一部分進(jìn)行提供,標準模版庫是一個(gè)基于模版的容器類(lèi)庫,保羅鏈表,列表,隊
列和堆棧。標準模版庫還包含許多常用的算法,包括排序與查找
標準模版庫的目的是提供對常用需求重新開(kāi)發(fā)的一種替代方法,標準模版庫已經(jīng)經(jīng)過(guò)測試
和調試,具有很搞的性能并且是免費的,最重要的是,標準模范庫是可重用的,當你知道如
何使用一個(gè)標準模版庫的容器以后,就可以在所有的程序中使用它而不需要重新開(kāi)發(fā)了
面向對象
1 面向對象技術(shù)的基本概念是什么?
對象,類(lèi)和繼承
2 下面的程序如果把靜態(tài)成員設為私有,該如何訪(fǎng)問(wèn)?
struct Test
{
Test(int){}
Test(){}
void fun(){}
static intGetHowMany()
{
return HowManyCats;
}
private:
static intHowManyCats;
};
intTest::HowManyCats = 20;//這里進(jìn)行初始化
可以通過(guò)共有的靜態(tài)成員函數進(jìn)行訪(fǎng)問(wèn)
如果靜態(tài)成員數據為public類(lèi)型則既可以通過(guò)類(lèi)也可以通過(guò)對象進(jìn)行訪(fǎng)問(wèn)
3 析構函數可以是內聯(lián)函數么?
析構函數可以為內聯(lián)函數
inline ~T(void);
4 構造函數能為虛函數么?
不行,虛調用是一種可以在只有部分信息的情況下工作的機制,特別允許我們調用一個(gè)只
知道接口而不知道其準確對象類(lèi)型的函數,但是如果要創(chuàng )建一個(gè)對象,勢必要知道對象的準
確類(lèi)型,因此構造函數不能為虛,構造函數是由系統在對象分配空間之前都調用的,而虛構
函數需要是創(chuàng )建了對象之后才能夠調用~`所以是不行的~但是,析構函數就可以~而且析
構函數經(jīng)常是用虛構函數
5 如果虛函數是非常有效的,我們是否可以把每個(gè)函數都聲明為虛函數?
不行,這是因為虛函數是有代價(jià)的,;由于每個(gè)虛函數的對象都必須維護一個(gè)v表,因此在
使用虛函數的時(shí)候都會(huì )產(chǎn)生一個(gè)系統開(kāi)銷(xiāo),如果僅是很小的類(lèi),而且不像培生其它的類(lèi),那
么就沒(méi)有必要使用虛函數
cout << Test::GetHowMany() << endl;
cout << b.GetHowMany() << endl;
cout << p->GetHowMany() << endl;
cout << b.HowManyCats << endl;
cout << b.GetHowMany << endl;
cout << Test::GetHowMany << endl;
cout<< p->GetHowMany << endl;

TT t;
cout << t.GetHowMany() << endl;
cout<< t.GetHowMany << endl;
這里的TT是繼承Test的

這里我們可以看出static函數也是繼承了的,但是始終只是保持一個(gè)副本
6 編寫(xiě)String的構造函數,析構函數和賦值函數
loadstring::~loadstring(void)
{
std::cout<< str << std::endl;
std::cout<< "析構了" << std::endl;
delete[] str;
}
loadstring::loadstring(constchar *loadstr)
{
if(loadstr == NULL)//這里特別重要,因為在vc中前M是段保護的,訪(fǎng)問(wèn)非法,即strlen出錯
{
str = new char[1];
*str ='\0';
}
else
{
int length = strlen(loadstr);
str = new char[length + 1];
strcpy(str,loadstr);
}
}
loadstring::loadstring(constloadstring &pramstring)
{
int length = strlen(pramstring.str);
str = new char[length + 1];
strcpy(str,pramstring.str);
}
loadstring& loadstring::operator=(const loadstring &pramstring)
{
if(this ==&pramstring)
return *this;
delete[] str;//這里很重要,要先釋放已經(jīng)存在的內存
int length = strlen(pramstring.str);
str = new char[length + 1];
strcpy(str,pramstring.str);
std::cout<< (void *)str << std::endl;
return *this;
}
多態(tài)的作用是什么呢?我們知道,封裝可以隱藏實(shí)現細節,使得代碼模塊化,集成可以擴展
已經(jīng)存在的代碼模塊(類(lèi)),它們的目的都是為了代碼重用,而多臺則是為了實(shí)現另外一個(gè)
目的---接口重用
繼承與接口
1 一個(gè)輸出問(wèn)題
class A
{
public:
virtual void f()
{
cout<< "A";
}
};
class B: public A
{
public:
virtual void f()
{
cout<< "B";
}
};
A *pa = new A();
pa->f();
B *pb = (B*)pa;
pb->f();
delete pa, pb;
//delete pb;
pa = new B();
pa->f();
pb = (B*)pa;
pb->f();

B *pb = (B*)pa;
該語(yǔ)句的意思是轉換pa為B類(lèi)型并新建一個(gè)指針pb,講pa復制到pb中,但是這里有一點(diǎn)
請注意,就是pa的指針始終滅有發(fā)生變化,所以pb也指向pb的f函數,這里并不存在覆
蓋問(wèn)題
三種繼承方式的總結:
1 公有繼承方式
基類(lèi)成員對其對象的可見(jiàn)性與一般類(lèi)及其對象的可見(jiàn)性相同,共有成員可見(jiàn),其他成員不
可見(jiàn),這里保護成員與私有成員相同
基類(lèi)成員對派生類(lèi)的可見(jiàn)性對派生類(lèi)來(lái)說(shuō),基類(lèi)的公有成員和保護成員可見(jiàn):基類(lèi)的共有
成員和保護成員作為派生類(lèi)的成員時(shí),它們都保持原有的狀態(tài);基類(lèi)的私有成員不可見(jiàn):基
類(lèi)的私有成員仍然是私有的,派生類(lèi)不可訪(fǎng)問(wèn)基類(lèi)中的私有成員
基類(lèi)成員對派生類(lèi)對象的可見(jiàn)性對派生類(lèi)對象來(lái)說(shuō),基類(lèi)的共有成員是可見(jiàn)的,其他成員
是不可見(jiàn)的
所以在共有繼承時(shí),派生類(lèi)的對象可以訪(fǎng)問(wèn)基類(lèi)中的公有成員,派生類(lèi)的成員函數可以訪(fǎng)
問(wèn)基類(lèi)中的公有成員和保護成員
2 私有繼承方式
基類(lèi)成員對其對象的可見(jiàn)性與一般類(lèi)及其對象的可見(jiàn)性相同,公有成員可見(jiàn),其他成員不
可見(jiàn)
基類(lèi)成員對派生類(lèi)的可見(jiàn)性對派生類(lèi)來(lái)說(shuō),基類(lèi)的公有成員和保護成員是可見(jiàn)的:基類(lèi)的
公有成員和保護成員都作為派生類(lèi)的私有成員,并且不能被這個(gè)派生類(lèi)的子類(lèi)所訪(fǎng)問(wèn);基類(lèi)
的私有成員是不可見(jiàn)的:派生類(lèi)不可訪(fǎng)問(wèn)基類(lèi)中的私有成員
基類(lèi)成員對派生類(lèi)對象的可見(jiàn)性對派生類(lèi)對象來(lái)說(shuō),基類(lèi)的所有成員都是不可見(jiàn)的
所以,在私有繼承時(shí),基類(lèi)的成員只能有直接派生類(lèi)訪(fǎng)問(wèn)
3 保護繼承方式
這種繼承方式與私有繼承方式的情況相同。兩者的區別僅僅在于對派生類(lèi)的成員而言,基
類(lèi)成員對其對象的可見(jiàn)性與一般類(lèi)及其對象的可見(jiàn)性相同,公有成員可見(jiàn),其他成員不可見(jiàn)
基類(lèi)成員對派生類(lèi)的可見(jiàn)性對于派生類(lèi)來(lái)說(shuō),基類(lèi)的共有成員和保護成員是可見(jiàn)的:基類(lèi)
的公有成員和保護成員都作為派生類(lèi)的保護成員,并且不能被這個(gè)派生類(lèi)的子類(lèi)所訪(fǎng)問(wèn);基
類(lèi)的私有成員是不可見(jiàn)的:派生類(lèi)不可訪(fǎng)問(wèn)基類(lèi)中的私有成員
基類(lèi)成員對派生類(lèi)對象的可見(jiàn)性對派生類(lèi)對象來(lái)說(shuō),基類(lèi)的所有成員都是不可見(jiàn)的
所以在保護繼承時(shí),基類(lèi)的成員也只能由直接派生類(lèi)訪(fǎng)問(wèn),而無(wú)法再往性愛(ài)繼承
一個(gè)小例子:
class Base
{
private://一般來(lái)說(shuō)應該變?yōu)?/span>protected,要不對派生類(lèi)就沒(méi)有用了
int t;
public:
Base(int x){t = x;};
};
class Derive: public Base
{
private:
int i;
public:
//Derive(int x, int y){i = x;};//這里出錯,要在子類(lèi)中設定初始成員變量要用下面的方式
Derive(int x, int y):Base(x){i = y;};//這是正確的
//void printTotal(){int tioal = i + Base::t;};//不能訪(fǎng)問(wèn)基類(lèi)的私有成員
};
一個(gè)關(guān)于純虛函數的例子:
class Shape
{
public:
Shape() =0{};
~Shape(){};
virtual void Draw() =0{};
virtual void DDD() =0;
};
class Circle: public Shape
{
void Draw(){};
void DDD(){};
};
Circle circle;
(1) 如果抽象類(lèi)的構造函數沒(méi)有定義則會(huì )出現鏈接錯誤
(2) 如果在抽象類(lèi)中定義了純虛函數,則必須在派生類(lèi)中進(jìn)行重寫(xiě),否則會(huì )出錯
虛函數的入口地址和普通函數有什么不同?
每個(gè)虛函數都在vtable中占了一個(gè)表項,保存著(zhù)一條跳轉到它的入口地址的指令(實(shí)際上就
是保存了它的入口地址)。當一個(gè)包含虛函數的對象(注意,不是對象的指針)被創(chuàng )建的時(shí)
候,它的頭部附加 個(gè)指針,指向btable中相應的位置,調用虛函數的時(shí)候,不管你是用什
么指針調用的,它先根據vtable找到入口地址再執行,從而實(shí)現了“動(dòng)態(tài)聯(lián)編”,而不像普
通函數那樣簡(jiǎn)單地跳轉到一個(gè)固定地址
C+如何阻止一個(gè)類(lèi)被實(shí)例化?
是用抽象類(lèi)或者將構造函數聲明為private
什么時(shí)候編譯器會(huì )生成默認的的復制構造函數?
只要自己沒(méi)寫(xiě),而程序需要就會(huì )生成
字符串
題目:編程實(shí)現字符串轉化為整型,不用atoi
string str;
int sum = 0;
cin >> str;
for(int i = 0; i < str.size(); i++)
{
sum = sum *10 + str[i] - '0';
}
cout<< sum << endl;
題目:重新 編寫(xiě)strcpy函數
Main函數中:
char strsrc[100]= "zhonghuarenmingongheguo";
char *str = new char[strlen(strsrc)+ 1];
char *p =strcpy1(str, strsrc);
cout << str << endl;
cout << strlen(p) << endl;
delete[] str;
函數實(shí)現:
char* strcpy1(char *strDest, const char* strSrc)
{
int i;
char *address = strDest;
for(i = 0; strSrc[i] != '\0';i++)
strDest[i]= strSrc[i];
strDest[i] = '\0';
return address;
}
為什么要返回一個(gè)char*呢?
為了實(shí)現鏈式表達式,返回具體指
int length = strlen(strcpy(strDest, “hello world”));
串拷貝(strcpy)和內存拷貝(memcpy)有什么不同?它們適合于哪種情況下使用?
Strcpy函數只能拷貝字符串,strcpy函數將源字符串的每個(gè)字節拷貝到目的字符串中,當遇
到字符串末尾的NULL字符時(shí),它會(huì )刪去該字符,并結束拷貝
Memcpy函數可以拷貝任意類(lèi)型的數據,因為并不是所有的數據都是以NULL結束的,所以
要為memcpy函數制定要拷貝的字節數,
extern void *memcpy(void *dest, void *src, unsigned int count);
在拷貝字符串時(shí)冗長(cháng)使用strcpy函數,在拷貝其它數據(如結構)時(shí),通常使用memcpy
例子:
char a = 256;
int d = a;
cout<< d + 1 << endl;//輸出為1
char數組溢出了,char類(lèi)型的變量賦值范圍為0~255,當把256賦值為a以后,超出了a
的有效取值范圍,此時(shí)a的實(shí)際值為0
例子:經(jīng)原始串中指定子串刪除(例如原始串為askdaskaskdaskg,指定刪除串為ask,最
后結果為ddg)
voidDeleteStr(string &Srcstr, const string&Substr)
{
stringResults("");
int i = 0;
while(i < Srcstr.size())
{
int j = i;
int k = 0;
while(k < Substr.size())
{
if(Srcstr[j] == Substr[k])//如果當前的匹配則查找兩個(gè)字符串的下一個(gè)字符
{
j++;
k++;
}
else//如果當前不相等則退出
break;
}
if(k == Substr.size())//得到了一個(gè)匹配的子串,就跳躍子串個(gè)字符后尋找下一個(gè)字符
i+= Substr.size();
else if(k == 0)//此時(shí)一個(gè)字符都沒(méi)有匹配到
{
Results+= Srcstr[i];
i++;
}
else//沒(méi)有匹配子串則當前已經(jīng)匹配完了k個(gè)字符,從下一個(gè)字符開(kāi)始尋找
{
for(int t = i; t <i + k; t++)
Results+= Srcstr[t];
i+= k;//從匹配的k個(gè)字符之后進(jìn)行匹配
}
}
cout <<"刪除子串以后的結果為:" << Results << endl;
}
例子:請寫(xiě)出一個(gè)函數來(lái)模擬c++中的strstr函數:該函數的返回值是主傳中字符子串的位
置以后的所有字符,請不要使用任何c程序已有的函數
string LeftSting(conststring &Srcstr, const string &Substr)
{
stringResults("");
int i = 0;
while(i < Srcstr.size())
{
int j = i;
int k = 0;
while(k < Substr.size())
{
if(Srcstr[j] == Substr[k])
{
j++;
k++;
}
else
break;
}
if(k == Substr.size())//找到了子串
{
for(int t = i; t <Srcstr.size(); t++)
Results+= Srcstr[t];
break;
}
else if(k == 0)//此時(shí)第一個(gè)不是匹配的
i++;
else//此時(shí)已經(jīng)匹配了k個(gè)字符
i += k;
}
return Results;
}
例子:存在一個(gè)數組。找出連續數之和最大的一段
void Find(int number[], intlength)
{
if(length == 0)//如果數組為空直接返回
return;
int Max = number[0], Acclulate = number[0];//初始化最大值為數組的第一個(gè)值
Node*Position = new Node[length];//用于存取到當前位置可能的最大值
NodeMaxPosition = {0, 0};//初始化最大的起始位置和終止位置
Position[0].begin= 0;
Position[0].end= 0;
for(int i = 1; i <length; i++)
{
if(Acclulate > 0)//前面的累積和大于
{
Position[i].begin= Position[i - 1].begin;
Position[i].end= Position[i - 1].end + 1;
Acclulate+= number[i];
if(Acclulate > Max)
{
Max= Acclulate;
MaxPosition= Position[i];
}
}
else//前面的累加和小于等于則重新設定起始位置和終止位置
{
Position[i].begin= i;
Position[i].end= i;
Acclulate= number[i];
if(number[i] > Max)
{
Max= number[i];
MaxPosition= Position[i];
}
}
}
cout <<"最大的值為:" << Max << endl;
cout <<"他們分別為:";
for(int i =MaxPosition.begin; i <= MaxPosition.end; i++)
cout<< number[i] << " ";
cout <<endl;
}
數據結構基礎
例子:編程實(shí)現單鏈表的刪除節點(diǎn)
Node* Find(Node* Head, intnum)
{
Node *p =Head;
Node *q = p;
while(p)
{
if(num != p->data)
{
q= p;//q用于保存要刪除的p結點(diǎn)的前一個(gè)結點(diǎn)
p= p->next;
}
else//此時(shí)找到了要刪除的結點(diǎn)
break;
}
if(!p)//此時(shí)沒(méi)有找到該數
return NULL;
else
{
if(p == Head)//刪除的是頭結點(diǎn)
{
Head= p->next;
delete p;
return Head;
}
else
{
q->next= p->next;
return Head;
delete p;
}
}
}
例子:編程實(shí)現單鏈表的逆置
Node* Reserve(Node *Head)
{
Node *p =Head;
if(!p)//空表
return NULL;
else if(!p->next)//單結點(diǎn)表
return NULL;
else
{
Node*q = p->next;
p->next= NULL;//首先處理頭結點(diǎn),設置為尾結點(diǎn)
while(q)//處理中間結點(diǎn)
{
Node*r = q->next;
q->next= p;
p= q;
q= r;
}
return p;
}
}
例子:編程實(shí)現單鏈表的插入
Node* InsertNode(Node *Head, intnum)
{
Node *newNode =new Node;
newNode->data= num;
if(!Head)//此時(shí)為空鏈表
{
newNode->next= NULL;
return newNode;
}
Node *p =Head;
Node *q =NULL;//q指向p結點(diǎn)之前的結點(diǎn)
while(p)//此時(shí)尋找位置
{
if(p->data < num)
{
q = p;
p= p->next;
}
else//此時(shí)找到了位置
break;
}
if(p == Head)//插入到頭結點(diǎn)之前
{
newNode->next= Head;
Head =newNode;
}
else if(!p)//插入到尾結點(diǎn)之后,此時(shí)q指向尾結點(diǎn)
{
q->next= newNode;
newNode->next = NULL;
}
else//插入到p結點(diǎn)和q結點(diǎn)之間
{
newNode->next= q->next;
q->next= newNode;
}
return Head;
}
例子:編程實(shí)現雙鏈表刪除結點(diǎn)(注意它和單鏈表刪除結點(diǎn)的情況有所不同)
Node* DoubleLink_DelNode(Node *Head, int num)
{
Node *p =Head;
if(!p)
return NULL;
while(p)
{
if(num != p->data)
p = p->next;
else
break;
}
if(!p)//沒(méi)有找到要刪除的結點(diǎn)
return NULL;
else
{
if(p == Head)//此時(shí)刪除的是頭結點(diǎn)
{
Head= Head->next;
delete p;
}
else if(p->next)//此時(shí)刪除的是中間結點(diǎn)
{
p->prev->next= p->next;
p->next->prev= p->prev;
delete p;
}
else//刪除的是尾結點(diǎn)
{
p->prev->next= NULL;
delete p;
}
}
return Head;
}
例子:編程實(shí)現雙鏈表的插入
Node* DoubleLink_InsertNode(Node *Head, int num)
{
Node *newNode= new Node;//初始化產(chǎn)生一個(gè)新結點(diǎn)
newNode->data= num;
newNode->prev= NULL;
newNode->next= NULL;
Node *p =Head;
Node *q =NULL;
while(p)
{
if(p->data < num)
{
q= p;
p= p->next;
}
else
break;
}
if(p == Head)//此時(shí)是在頭結點(diǎn)之前進(jìn)行插入
{
newNode->next= p;
p->prev= newNode;
Head =newNode;
}
else if(!p)//在尾結點(diǎn)之后進(jìn)行插入
{
q->next= newNode;
newNode->prev= q;
}
else//在中間進(jìn)行插入
{
p->prev->next= newNode;
newNode->prev= p->prev;
newNode->next= p;
p->prev= newNode;
}
return Head;
}
例子:如何證明一個(gè)表是循環(huán)鏈表
link * p,*q;
p=head;
q=p->next;
while(q&&q->next&&p!=q) //qor q->next ==NULL時(shí)無(wú)環(huán),q==q時(shí)有環(huán)
{
p=p->next;
q=q->next->next;
}
if(p==q)
cout<<"have ring";
else
cout<<"noring";
例子:樸素字符串匹配算法的一點(diǎn)改進(jìn)(子串必須各個(gè)字符不相等),時(shí)間復雜度O(n)
int Match(const string &str1, conststring &str2)
{
int i = 0;
while(i < str1.size())
{
int j = i;
int t = 0;
while(t < str2.size())
{
if(str1[j] == str2[t])
{
j++;
t++;
}
else
break;
}
if(t == str2.size())//此時(shí)找到了子串
return i;
if(t == 0)//當第一個(gè)字符都不匹配的時(shí)候就取下一個(gè)
i++;
else//前面有部分字符匹配成功的情況
i += t;
}
return -1;
}
另外一種方法:
int i = 0, q =-1;//q表示在當前字符之前已經(jīng)正確匹配到第幾個(gè)字符了
while(i <str1.size())
{
if(str1[i] == str2[q + 1])
q +=1;
else if(q != -1)//當前已經(jīng)匹配了個(gè)以上的字符
{
q = -1;
i--;;//保持源字符串的當前字符不變
}
if(q == str2.size() - 1)//得到一組解
return i - q;
i++;
}
return -1;
例子:實(shí)現隊列的出隊與入隊
//數據入隊列
Node *EnQueue(Node *head, Node **tail, int data)
{
//創(chuàng )建一個(gè)新結點(diǎn)
Node *p = new Node;
p->data =data;
p->next =NULL;
if(head == NULL)//此時(shí)為空隊列
{
head = p;
*tail= p;
}
else
{
(*tail)->next= p;
*tail= p;
}
return head;
}
//刪除頭結點(diǎn)
Node* DeQueue(Node *head)
{
if(!head)//頭結點(diǎn)為空
return NULL;
else
{
Node*p = head;
head =head->next;
delete p;
}
return head;
}
聯(lián)系客服