1. 除了靜態 static 資料成員外,資料成員不能在類體中被顯式地初始化。
例如 :
class First {
int memi = 0; // 錯誤
double memd = 0.0; // 錯誤
};
類的資料成員通過類的建構函式進行初始化。
2. 我們可以聲明一個類但是並不定義它.
例如:
class Screen; // Screen 類的聲明
這個聲明向程式引入了一個名字 Screen,指示 Screen為一個類類型。
但是我們只能以有限的方式使用已經被聲明但還沒有被定義的類類型,如果沒有定義類那麼我們就不能定義這類類型的對象。因為類類型的大小不知道,編譯器不知道為這種類類型的對象預留多少儲存空間。
但是,我們可以聲明指向該類類型的指標或引用,允許指標和引用是因為它們都有固定的大小, 這與它們指向的對象的大小無關,但是,因為該類的大小和類成員都是未知的,所以要等到完全定義了該類,我們才能將解引用操作符( * )應用在這樣的指標上,或者使用指標或引用來指向某一個類成員。
只有已經看到了一個類的定義,我們才能把一個資料成員聲明成該類的對象。在程式文本中還沒有看到該類定義的地方,資料成員只能是該類類型的指標或引用。例如下面是類StackScreen的定義,它有一個資料成員是指向 Screen類的指標,這裡 Screen只有聲明沒有定義:
class Screen; // 聲明
class StackScreen {
int topStack;
// ok: 指向一個 Screen 對象
Screen *stack;
void (*handler)();
};
因為只有當一個類的類體已經完整時,它才被視為已經被定義,所以一個類不能有自身類型的資料成員,但是,當一個類的類頭被看到時,它就被視為已經被聲明了,所以一個類可以用指向自身類型的指標或引用作為資料成員。例如 :
class LinkScreen {
LinkScreen iLinkScreen; // 錯誤:一個類不能有自身類型的資料成員
LinkScreen *next;
LinkScreen *prev;
};
3. inline和非inline成員函數
在類體中定義的成員函數預設是inline函數,可以顯示的加上inline;
通常,在類體外定義的成員函數不是 inline 的,但是,這樣的函數也可以被聲明為 inline函數。可以通過顯式地在類體中出現的函式宣告上使用關鍵字 inline 或者通過在類體外出現的函數定義上顯式使用關鍵字 inline,或者兩者都用。
4. const 成員函數
通常,程式中任何試圖修改 const 對象的動作都會被標記為編譯錯誤
const char blank = ' ';
blank = '\n'; // 錯誤
但是,程式通常不直接修改類對象,又是在必須修改類的對象時,才調用公有成員函數集來完成,為尊重類對象的常量性,編譯器必須區分不安全與安全的成員函(即區分試圖修改類對象與不試圖修改類對象的函數 ),例如:
const Screen blankScreen;
blankScreen.display(); // 讀類對象
blankScreen.set( '*' ); // 錯誤: 修改類對象
類的設計者通過把成員函式宣告為 const 以表明它們不修改類對象,例如 :
class Screen {
public:
char get() const { return _screen[_cursor]; }
// ...
}
只有被聲明為 const 的成員函數才能被一個const 類對象調用,關鍵字 const 被放在成員函數的參數表和函數體之間,對於在類體之外定義的const 成員函數,我們必須在它的定義和聲明中同時指定關鍵字 const。
把一個修改類資料成員的函式宣告為 const 是非法的,例如,在如下簡化的 Screen定義中:
class Screen {
public:
int ok() const { return _cursor; }
void error( int ival ) const { _cursor = ival; }
// ...
private:
string::size_type _cursor;
// ...
};
ok()的定義是一個有效 const 成員函數定義,因為它沒有改變_cursor 的值,但是 error() 的定義修改了_cursor 的值,因此它不能被聲明為一個 const 成員函數,這個函數定義將導致下面的編譯器錯誤訊息:
error: cannot modify a data member within a const member function
但是,把一個成員函式宣告為const 並不能阻止程式員可能做到的所有修改動作,把一個成員函式宣告為 const 可以保證這個成員函數不修改類的資料成員 。但是,如果該類含有指標,那麼在 const 成員函數中就能修改指標所指的對象,編譯器不會把這種修改檢測為錯誤,例如 :
#include <cstring>
class Text {
public:
void bad( const string &parm ) const;
private:
char *_text;
};
void Text::bad( const string &parm ) const
{
_text = parm.c_str(); // 錯誤: 不能修改 _text
for ( int ix = 0; ix < parm.size(); ++ix )
_text[ix] = parm[ix]; // 不好的風格, 但不是錯誤的
}
儘管_text 不能被修改,但是_text 的類型是 char* 在類 Text 的 const 成員函數中可以修改_text 指向的字元,成員函數 bad()反映了一種不良的程式設計風格。
const 成員函數可以被相同參數表的非 const 成員函數重載,例如:
class Screen {
public:
char get(int x, int y);
char get(int x, int y) const;
// ...
};
在這種情況下 類對象的常量性決定了調用哪個函數 :
int main() {
const Screen cs;
Screen s;
char ch = cs.get(0,0); // 調用 const 成員
ch = s.get(0,0); // 調用非 const 成員
}
建構函式和解構函式是兩個例外,即使建構函式和解構函式不是const 成員函數,const 類對象也可以調用它們。當建構函式執行結束,類對象已經被初始化時,類對象的常量性就被建立起來了,解構函式一被調用,常量性就消失。所以一個 const 類對象從構造完成時刻到析構開始時刻,這段時間內被認為是const。