-1,程序中各段的意義如下:
.text(代碼段):用來存放可執(zhí)行文件的操作指令,也就是說是它是可執(zhí)行程序在內(nèi)存種的鏡像。
.data(數(shù)據(jù)段):數(shù)據(jù)段用來存放可執(zhí)行文件中已初始化全局變量,也就是存放程序靜態(tài)分配的變量和全局變量。
.rodata(只讀段):該段保存著只讀數(shù)據(jù),在進(jìn)程映象中構(gòu)造不可寫的段。
.bss(BSS段):BSS段包含了程序中未初始化全局變量,在內(nèi)存中 bss段是否全部置零依賴于系統(tǒng)的實現(xiàn)。
0, 深入掌握C++以及靈活分析C++中問題的必備知識: C++對象建立語意學(xué)(呵呵,模仿一下候捷的語氣)
詳細(xì)內(nèi)容見拙著(正在撰寫,暫時不能訪問,暫時參閱<深度探索C++對象模型> 和<潘凱:C++對象布局及多態(tài)實現(xiàn)的探索>系列):
http://blog.csdn.net/I_Love_CPP/archive/2007/09/07/1775472.aspx1,阻止對象的生成
方法1:純虛類
方法2:構(gòu)造函數(shù)申明為protected.
2,阻止對象的派生(模擬Java中的final類)
class A{
private:
A(){
}
public:
A(int i){
}
};
A a(1);//正確
class B:public A{
public:
B(){//錯誤
};
};
B b;//錯誤
3,使用局部類和嵌套類實現(xiàn)類的隱藏(有點類似Java中的內(nèi)部類)
當(dāng)希望充分利用OO思想,但是又不想把類暴露給外部時可以用局部類。
int main(int argc, char* argv[])
{
//這個類只是我用來調(diào)試用,寫的根本見不得人,因此用局部類:)
class A{
private:
A(){
}
public:
A(int i){
}
};
A a(1);
system("pause");
return 0;
}
//類似的,嵌套類是在類中再定義一個類
4,運行時類別識別RTTI技術(shù):
在JAVA中可以用instanceof運算符以及反射機(jī)制來進(jìn)行RTTI,
C++中的RTTI技術(shù)呢?
答案是
1)使用使用type_info類,
但因為type_info只有一個私有的拷貝構(gòu)造函數(shù),
必須通過type_id運算符創(chuàng)建一個臨時變量。
SoundableAnimal* pAnimals[2];
pAnimals[0] = new Cat;
pAnimals[1] = new Dog;
for(int i = 0; i < 2; ++i)
{
if(typeid(*pAnimals[i]) == typeid(Cat))//注意,需要把/GR編譯開關(guān)打開
pAnimals[i]->Shout();
delete pAnimals[i];
}
2)在某些要求不要的情況下,可以用dynamic_cast。
當(dāng)dynamic_cast遇到一個不屬于自己的類型時(即不滿足is-a關(guān)系)就會返回空指針或者拋出異常。
5,return返回一個對象時究竟做了什么?
從一個網(wǎng)友的問題說起:
他寫了一個矩陣類,用來實現(xiàn)矩陣的轉(zhuǎn)置:
//Declare
class Matrix
{
public:
Matrix();
virtual ~Matrix();
Matrix(int n,int m);
Matrix(const Matrix & M2);
Matrix Transpose() const;
private:
double *p;
int m;
int n;
static int g_num;
};
//Implements:
Matrix::Matrix(){p = NULL;}
Matrix::~Matrix(){if(p)delete[] p;}
Matrix::Matrix(int nTemp, int mTemp)
{
n = nTemp;
m = mTemp;
p = new double[n*m];
}
Matrix::Matrix(const Matrix & M2)
{
unsigned int i,j;
if (M2.p!=0)
{
delete []p;//(1)注意這兒,有問題
m=M2.m;
n=M2.n;
p=new double [m*n];
if (p!=0)
{
for (i=0;i<m;++i)
for (j=0;j<n;++j)
p[i*n+j]=M2.p[i*n+j];
}
}
}
Matrix Matrix::Transpose() const
{
Matrix temp(n,m);
unsigned int i,j;
for (i=0;i<m;++i)
for (j=0;j<n;++j)
temp.p[j*m+i]=p[i*n+j];
return temp;//(2)
}
/////////////////////////////////////////////////////////////////////////////////////////////
程序會出錯,指向(1)處。
一般而言大家有個習(xí)慣,動態(tài)分配內(nèi)存時先要釋放已經(jīng)分配的內(nèi)存,而他在默認(rèn)構(gòu)造函數(shù)中初始化了p = NULL;的,
因此delete NULL看似應(yīng)該不會出現(xiàn)問題。
但是注意:
1)拷貝構(gòu)造函數(shù)并不會調(diào)用默認(rèn)構(gòu)造函數(shù);
2)(2)處return 返回一個對象時,編譯器會自動生成一個臨時對象。只要是返回對象而不是指針或者引用,那么
此臨時對象是必然會創(chuàng)建的。也就是說return temp等價于return Matrix(temp);
但是,拷貝構(gòu)造函數(shù)中并初始化p,因此p指向了一塊任意的內(nèi)存區(qū),當(dāng)然調(diào)用delete[] p;會出錯。
3)在拷貝構(gòu)造函數(shù)中以及所有的構(gòu)造函數(shù)中考慮“釋放已經(jīng)分配的內(nèi)存”是否必要呢?
我的答案是完全不必要。因為對象只是在構(gòu)造函數(shù)成功后才存在,那么哪兒有“已經(jīng)分配的內(nèi)存“之說呢”。
因此1)處的delete[] p;應(yīng)該刪掉。程序就正確了。
6,構(gòu)造函數(shù)失敗會怎樣?
從5.的討論涉及到了一個話題”構(gòu)造函數(shù)成功“,為了討論的完備性,我問自己”構(gòu)造函數(shù)失敗會怎樣“?
剛好我讀到過這方面的咚咚,我引用Herb Sutter《More Exceptional C++》文中的回答:
1)構(gòu)造函數(shù)正常返回,這種情況對象真是存在;
2)構(gòu)造函數(shù)拋出異常后退出,這種情況下對象根本從來未存在過(因此也不會調(diào)用析構(gòu)函數(shù))。
再沒有其它情況了。
7,字節(jié)存儲順序
字節(jié)存儲順序是指系統(tǒng)中應(yīng)用的字符的順序(在內(nèi)存中存儲16進(jìn)制場字符是按0xAABBCCDD的順序還是按照0xDDCCBBAA的字符順序)。
測試字符的存儲順序的具體方法是直接把長字符數(shù)值1(0x000001)存到內(nèi)存中,然后檢查1在高位還是低位。
bool little_endian()
{
unsigned int i = 0x01;
return(*(unsigned char*) &i == 0x01);
}
void cpu_info()
{
if(little_endian())
printf("LITTLE ENDIAN");
else
printf("BIG ENDIAN");
printf("%d bit architecture\n",sizeof(int) * 8);
}
8.const與volatile:(事物的2個面 )
關(guān)鍵字volatile有什么含意 并給出三個不同的例子。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設(shè)這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應(yīng)用中被幾個任務(wù)共享的變量
8'
1). 一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數(shù)有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應(yīng)該試圖去修改它。 (事物的多面性)
2). 是的。盡管這并不很常見。一個例子是當(dāng)一個中服務(wù)子程序修該一個指向一個buffer的指針時。
3). 這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
9.const與mutable (盾與矛)
const:只讀
mutable:A mutable data member is a member that is never const, even when
it is the data member of a const object. A mutable member can always be updated, even in a const
member function. <<c++ primer>>
(最終,是矛戳穿了盾...)
多問一句:const 變量/對象是在.rodata段嗎?
10. const_cast, static_cast,reinterpret_cast和dynamic_cast
const_cast,
as itsname implies, casts away the const-ness of its expression (and also the volatility of a volatile object).
For example:
extern char *string_copy( char* );
const char *pc_str;
char *pc = string_copy( const_cast< char* >( pc_str ));
An attempt to cast away const-ness using either of the other three forms results in a compile-time error.
Similarly, it is a compile-time error to use the const_cast notation to perform a general type conversion.
static_cast,
Any type conversions that the compiler performs implicitly can be made explicit through use of a
static_cast.
reinterpret_cast,
A reinterpret_cast generally performs a low-level reinterpretation of the bit pattern of its operands,
and its correctness in large part depends on the active management of the programmer.
For example, in the following cast
complex<double> *pcom;
char *pc = reinterpret_cast< char* >( pcom );
the programmer must never lose sight of the actual object addressed by pc.
dynamic_cast
The dynamic_cast supports the run-time identification of class objects addressed either by a pointer or
reference.
如果覺得英文看的不爽,這有一個網(wǎng)友總結(jié)還不錯:
static_cast
用法:static_cast < type-id > ( expression )
該運算符在expression轉(zhuǎn)換為type-id類型(編譯時),但沒有運行時類型檢查來保證轉(zhuǎn)換的安全性。它主要有如下幾種用法:用于類層次結(jié)構(gòu)中基類和子類之間指針或引用的轉(zhuǎn)換。進(jìn)行上行轉(zhuǎn)換(把子類的指針或引用轉(zhuǎn)換成基類表示)是安全的;進(jìn)行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成子類表示)時,由于沒有動態(tài)類型檢查,所以是不安全的。
用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char,把int轉(zhuǎn)換成enum。這種轉(zhuǎn)換的安全性也要開發(fā)人員來保證。
把空指針轉(zhuǎn)換成目標(biāo)類型的空指針。
把任何類型的表達(dá)式轉(zhuǎn)換成void類型。
注意:static_cast不能轉(zhuǎn)換掉expression的const、volitale、或者_(dá)_unaligned屬性。
dynamic_cast
用法:dynamic_cast < type-id > ( expression )
該運算符把expression轉(zhuǎn)換成type-id類型的對象。Type-id必須是類的指針、類的引用或者void *;如果type-id是類指針類型,那么expression也必須是一個指針,如果type-id是一個引用,那么expression也必須是一個引用。
dynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換,還可以用于類之間的交叉轉(zhuǎn)換。
在類層次間進(jìn)行上行轉(zhuǎn)換時,dynamic_cast和static_cast的效果是一樣的;在進(jìn)行下行轉(zhuǎn)換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。
class B{
public:
int m_iNum;
virtual void foo();
};
class D:public B{
public:
char *m_szName[100];
};
void func(B *pb){
D *pd1 = static_cast<D *>(pb);
D *pd2 = dynamic_cast<D *>(pb);
}
在上面的代碼段中,如果pb指向一個D類型的對象,pd1和pd2是一樣的,并且對這兩個指針執(zhí)行D類型的任何操作都是安全的;但是,如果pb指向的是一個B類型的對象,那么pd1將是一個指向該對象的指針,對它進(jìn)行D類型的操作將是不安全的(如訪問m_szName),而pd2將是一個空指針。另外要注意:B要有虛函數(shù),否則會編譯出錯;static_cast則沒有這個限制。這是由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數(shù)表(關(guān)于虛函數(shù)表的概念,詳細(xì)可見<Inside c++ object model>)中,只有定義了虛函數(shù)的類才有虛函數(shù)表,沒有定義虛函數(shù)的類是沒有虛函數(shù)表的。
另外,dynamic_cast還支持交叉轉(zhuǎn)換(cross 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); //copile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
在函數(shù)foo中,使用static_cast進(jìn)行轉(zhuǎn)換是不被允許的,將在編譯時出錯;而使用 dynamic_cast的轉(zhuǎn)換則是允許的,結(jié)果是空指針。
reinpreter_cast
用法:reinpreter_cast<type-id> (expression)
type-id必須是一個指針、引用、算術(shù)類型、函數(shù)指針或者成員指針。它可以把一個指針轉(zhuǎn)換成一個整數(shù),也可以把一個整數(shù)轉(zhuǎn)換成一個指針(先把一個指針轉(zhuǎn)換成一個整數(shù),在把該整數(shù)轉(zhuǎn)換成原類型的指針,還可以得到原先的指針值)。
該運算符的用法比較多。
const_cast
用法:const_cast<type_id> (expression)
該運算符用來修改類型的const或volatile屬性。除了const 或volatile修飾之外, type_id和expression的類型是一樣的。
常量指針被轉(zhuǎn)化成非常量指針,并且仍然指向原來的對象;常量引用被轉(zhuǎn)換成非常量引用,并且仍然指向原來的對象;常量對象被轉(zhuǎn)換成非常量對象。
Voiatile和const類試。舉如下一例:
class B{
public:
int m_iNum;
}
void foo(){
const B b1;
b1.m_iNum = 100; //comile error
B b2 = const_cast<B>(b1);
b2. m_iNum = 200; //fine
}
上面的代碼編譯時會報錯,因為b1是一個常量對象,不能對它進(jìn)行改變;使用const_cast把它轉(zhuǎn)換成一個常量對象,就可以對它的數(shù)據(jù)成員任意改變。注意:b1和b2是兩個不同的對象。
9'. 如果確實想用c-style簡易的強(qiáng)制轉(zhuǎn)換在c++中推薦使用新式類型轉(zhuǎn)換符:
// C++ function cast notation
type (expr); 因為這樣的話就可以為類type重載()符,進(jìn)而使用上面的4種顯式類型轉(zhuǎn)換符
// C-language cast notation
(type) expr;