聲明與定義分離
標頭檔:用於記錄聲明代碼
模組檔案(c檔案/cpp檔案):用於記錄實現代碼
絕大多數串連程式是把一個模組中的所有函數、變數當作整體串連到執行程式中。
只要引用了模組中的一個名字,整個模組就進入執行檔案(如果模組10M,則所有引用過這個模組的執行檔案都要超過10M)
多個函數的實現,應該分到不同的模組
可(但不一定)放在一個模組的函數
相互調用的函數
一定會被同時調用的函數
open()/close(), new/delete
非常相關的函數
不可放在一個模組的函數
不相關的函數
如果不知道如何做,則每個模組只放一個函數
聲明與標頭檔
不能把所有聲明放到一個標頭檔
主要是維護的問題
相關的函數放在一個標頭檔
不相關的函數放在不同的標頭檔
如果不知道如何做,每個模組對應一個標頭檔
使用#ifndef...#define...#endif語句可以防止標頭檔被多次包含。
對象
對象的實體被定義時,應初始化成確定狀態
避免在非確定狀態對對象進行操作
可能導致難以預料的錯誤
對象死亡時,應清除所有可能的影響
避免對系統造成不可恢複的影響
資源泄漏
對應的就是類的建構函式和解構函式。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
與記憶體相關的內容:
變數(variable)有不同類型,可能佔據不同數量的記憶體單元。但是一個變數佔據的記憶體單元總是連續的。
變數的地址(variable address)給變數分配的記憶體單元中最小的地址,又叫做“首地址”。
數組:記憶體中的分布,連續的多個記憶體單元。
記憶體是寶貴的資源,不能浪費!
自動(auto)變數:編譯器在需要的地方自動插入分配和釋放相應記憶體單元的指令,無需程式員管理。
棧(stack):管理函數調用和返回資訊,管理自動變數,空間比較小,32位平台上,一般1MB左右。
堆(heap):現代電腦的記憶體空間是比較大的,32位平台:最多4GB,棧只佔用很小一部分:1MB,一般通過new操作符分配的記憶體都屬於堆。
指標的大小通常和整數的大小相同,32位平台,通常是32位,或4位元組,與指標所指向的內容的大小無關。
指標指向內容的範圍:由程式員控制,不再使用的時候,要釋放。如果忘記釋放,就會產生記憶體泄露;如果尚未分配記憶體就訪問其指向的內容:非法指標訪問。如果訪問的單元超出了分配的範圍:指標越界。
對指標操作的一些建議:
總是把指標初始化成空NULL。
用C++類防止記憶體流失:
建構函式:在定義變數的一開始被自動調用
解構函式:在變數被釋放前被自動調用
class TCharArray
{
public:
char *img; //要管理的記憶體
int len; //分配的記憶體大小
TCharArray()
{
img = NULL; //初始化為空白狀態
len = 0;
}
……
~TCharArray() //解構函式
{
Close(); //調用記憶體釋放函數(很重要!)
}
void Close()
{
if(img!=NULL) //測試是否已經分配
{
delete []img; //如果是,則釋放,並恢複成空狀態
img = NULL;
len = 0;
}
}
……
void Alloc(int size) //記憶體配置函數
{
Close(); //用記憶體釋放函數(很重要!)
img = new char[size]; //分配記憶體,設定成合法狀態
len = size;
}
……
char operator[](int inx) //定義operator[]操作符重載函數,只能夠讀取資料對象的傳回值,不能實現在client代碼中做修改
{
assert( (i>=0) && (i<len) );
return img[inx];
}
char &operator[](int inx) //返回引用,這樣就可以在client代碼中,直接對TcharArray類的資料對象就可以直接修改了。
{
assert( (i>=0) && (i<len) );
return img[inx];
}
};
下面是一個小例子,把上面的想法都實現了一下:
#include<iostream>
#include<cassert>
#include<string>
using namespace std;
template<typename T> //類模板
class Array{
public :
Array(){
len=0;
intPointer=NULL;
}
void allocate(int size){
deAllocate();
len=size;
intPointer=new T[size];
memset(intPointer,0,size*sizeof(T)); //初始化
}
// int operator[](int pos){ //重載operator[],這樣可以直接通過Array[]形式訪問元素
//
// assert((pos>=0) && (pos<len));
// return intPointer[pos];
// }
int& operator[](int pos){ //重載operator[],返回引用,這樣就可以直接修改資料對象的值了
assert((pos>=0) && (pos<len));
return intPointer[pos];
}
~Array(){
deAllocate(); //釋放對象佔用的記憶體空間
}
private:
void deAllocate(){
if( intPointer!=NULL){
delete [] intPointer; //delete [] intPointer是通用的
len=0;
intPointer=NULL;
}
}
int len;
T *intPointer;
};
template<class T> //模板函數
void exchange(T& t,T& s){
T tmp=s;
s=t;
t=tmp;
}
int main(){
Array<int> a;
a.allocate(5) ;
cout<<a[1]<<endl;
//直接使用[]操作符,並就行修改,如果沒有對operator[]返回引用值的版本進行重載,就會出現:
//[Error] C:/Users/Administrator/Desktop/test.cpp:73: error: non-lvalue in assignment
a[1]=8;
cout<<a[1]<<endl;
int c=4,d=5;
exchange(c,d) ;
//輸出5 4
cout<<c<<" "<<d<<" "<<endl;
return 0;
}
volatile: 如一個變數可能被多個線程修改,應定義為volatile
this: C++成員函數中指向當前對象的指標
全域變數:程式的整個生存周期都存在,常量和全域資源的管理資料
靜態變數:程式的整個生存周期記憶體在,當函數被遞迴調用時,也僅有一個變數。注意靜態變數和全域變數都是一樣的,在程式的整個生命週期中都存在。
棧:管理函數調用和返回,分配局部變數,不能返回指向局部變數的指標或引用!
棧:
一般不太大(1MB或以下)
大塊的記憶體應該程式員主動管理,不應自動管理
不要定義大的局部數組
堆:
程式員主動管理的記憶體
主要的儲存資源
使用堆時要注意的一些地方:
盡量用sizeof計算變數的大小,特別是結構的大小,結構的大小在不同的編譯選項下面可能有差異。
void * 類型: 可以指向任何類型的指標
const:
1、不可改變的量可以被定義為常量,以防止非正常修改變數的值,並且提供編譯最佳化的可能性
2、不應改變的函數參數,主要是指標和引用
3、常量指標和指向常量的指標:
常量指標:指標本身不可改變
char * const FileName;
FileName[0] = 0; (對)
FileName = newFileName; (錯)
指向常量的指標:指標可改變,但內容不可改變
const char * FileName;
FileName[0] = 0; (錯)
FileName = newFileName; (對)
4、常成員函數:不修改類變數的函數