C++基礎教程(總結)
1、C++語言支持的新特性
注:1983年夏,“帶類的C”被正式命名為“C++”
2、常量
數值常量、字符常量、符號常量、枚舉常量。。。
3、變量
變量名代表內存中的一個存儲單元,在程序編譯連接時由系統給每一個變量分配一個地址。通過變量名找到相應的存儲單元,從中讀取數據
變量名的命名規則
未對變量賦初值,其值是一個不可預測的值
初始化不是在編譯時完成的(靜態存儲變量和外部變量除外),而是在運行時執行本函數時賦予初值的
4、數據類型
C++編譯系統把十進制小數形式的浮點數默認按雙精度處理
浮點數在內存中以指數形式存放
若一個字符串的最後一個為“\”,則它是續行符
在計算機中負數的運算和顯示以補碼的形式,16的原碼為 0001 0000,~16為 1110 1111 ,則~16為負數。因此,~16補碼為 1000 1010+1=1000 1011=-11
不同類型數據間的轉換(賦值兼容):
標准類型數據間的轉換
隱式轉換;顯示轉換:類型名(數據) / (類型名)數據
用轉換構造函數進行類型轉換
用類型轉換構造函數進行類型轉換
下面為常量、變量以及數據類型的測試代碼:
復制代碼
#include<iostream>
using namespace std;
#define PI 3.1415926
int main()
{
const int NUM=6;
enum color{red,yellow,green,black,white};
cout<<green<<endl;
int a;//未初始化,值不確定
cout<<"a="<<a<<endl;
float b=1.23456789;
cout<<b<<endl;
long double c=1.23456789;
cout<<c<<endl;
cout<<sizeof(short int)<<"Byte"<<'\t'
<<sizeof(int)<<"Byte"<<'\t'
<<sizeof(float)<<"Byte"<<'\t'
<<sizeof(double)<<"Byte"<<'\t'
<<sizeof(long double)<<"Byte"<<endl;
double d=1.23f;
cout<<d<<endl;
char ch=6;
cout<<ch<<endl;
char ch1=0141; //97
cout<<"ab\\\"cd\"\t\101\x41\0cd"<<'\0'<<ch1<<endl;
return 0;
}
復制代碼
5、指針
一個變量的地址即該變量的指針
指針變量的值是地址(指針)
凡是存放在內存中的程序和數據都有一個地址,可以用它們占用的那片存儲單元中的第一個存儲單元的地址表示
& 和 * 的使用:
有關指針的數據類型:
指針運算:
指針變量加減一個整型值
指針變量的賦值
指針變量可以賦空值,但不能賦整數
指向同一類型的指針變量相互賦值時,後期操作可能造成指針懸掛問題(一指針所指空間被釋放,另一指針所指空間亦被釋放),在釋放一指針所指的空間時,一定要確保所有指向該空間的指針都已不再需要,以防止指針懸掛
若要對不同類型的指針變量賦值,應用強制類型轉換
指針變量的比較
指針變量的相減,相加無意義
指針的一些注意事項:
所有指針,包括訪問本地堆的指針,在 Win32 下都是 32 位(4個字節)
一般的C++編譯系統為每一個指針變量分配4個字節的存儲單元,用來存放變量的地址
在使用指針變量之前須先給它賦一個指向合法具體對象的地址值,否則可能破壞有用的單元的內容
指針的優缺點:
優點
在調用函數時,若改變被調用函數中某些變量的值,這些值能為主調函數使用,可通過函數的調用得到多個可改變的值
可以實現動態存儲分配
占內存少,運算速度快
缺點
容易產生難以發現的錯誤
數組與指針:
指針數組作為main函數的形參:
在DOS狀態下輸入:命令名 參數….
下面為相應的代碼:
復制代碼
#include<iostream>
using namespace std;
int main(int argc, char *argv[])
{
while(argc > 1)
{
++argv;
cout<<*argv<<endl;
--argc;
}
return 0;
}
復制代碼
動態存儲分配:
6、引用
int &b = a; //聲明b是a的引用(別名)
聲明引用時必須使之初始化
聲明b是a的引用後,b不能再作為其他變量的引用,且b和a占同一存儲單元,具有同一地址(指針變量需另外開辟空間)
主要用來作為函數參數
調用函數時,實參是變量名不是地址,而系統向形參傳遞的是地址不是其值
7、字符串
對一個字符串常量,系統會在所有字符的後面加一個’\0’作為結束符
字符串的長度比對應的字符數組(包含一個空字符’\0’)少1字節,字符串數組每一個字符串元素不包含’\0’
編譯系統為每一個字符串變量(string類的一個對象)分配固定長度的存儲單元(VC為16個字節,GCC為4個字節),存放字符串的地址,即字符串的指針
訪問字符串的三種方法:
字符數組,即C-string方法
字符串變量,即string方法(C++)
字符指針
指針、引用、字符串測試源代碼:
復制代碼
#include<iostream>
using namespace std;
int main()
{
char str[] = "Hello";
string m_str = "Hello";
string name[5] = {"zhang","li","gao"};
char *pstr = "Hello";
while(*pstr!='\0')
{
cout<<*pstr;
pstr++;
}
cout<<endl;
cout<<"字符數組的長度:" <<sizeof(str) <<"Byte"<<endl;
cout<<"字符串的長度: " <<strlen("Hello")<<"Byte"<<endl;
//每一個字符串變量被編譯系統分配固定長度的存儲單元,VC++為16個字節
cout<<"字符串變量的長度:"<<sizeof(m_str) <<"Byte"<<endl;
cout<<sizeof(string)<<'\t'<<sizeof(name)<<endl;
//一般的C++編譯系統為每一個指針變量分配4個字節的存儲單元,用來存放變量的地址
cout<<sizeof(pstr)<<endl;
cout<<"**************************************************"<<endl<<endl;
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int *p=arr;
for(int i=0;i<10;i++)
{
// cout<<arr[i]<<'\0';
cout<<*(p+i)<<' ';
}
cout<<endl;
int *p1 = new int(10);
cout<<*p1<<endl;
delete p1;
return 0;
}
復制代碼
8、函數
函數名代表函數的入口地址即函數的指針,函數的指針在編譯時被分配
當數組作函數形參時,C++編譯系統將形參數組名一律作為指針變量來處理
實參數組名代表一個固定的地址,或說是指針常量,而形參數組名為指針變量
根據函數能否被其他源文件調用,將函數區分為內部函數和外部函數
函數參數傳遞:
內置函數:
在編譯時將調用的函數的代碼直接嵌入到主調函數中,而不是將流程轉出去,這種嵌入到主調函數中的函數稱為內置函數(inline function),又稱內嵌(內聯)函數
在函數首行的左端加一個關鍵字inline,可在聲明和定義時同時寫,也可只在其中一處寫,效果相同
可節省運行時間,但增加了目標程序的長度
只有那些規模小而又被頻繁調用的簡單函數,才適合聲明為inline函數;而且對函數作inline聲明對編譯系統只是建議性的
函數重載、覆蓋與隱藏:
9、枚舉
enum weekday{sun, mon, tue, wed, thu, fri, sat};//聲明
weekday workday, week_end; //定義枚舉變量
在C++中允許不寫enum(一般也不寫),花括號中的稱為枚舉元素或枚舉常量,C++編譯按定義時的順序對它們賦值為0,1,2,3,…
可在聲明枚舉類型時指定枚舉元素的值:
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};
編譯系統自動按順序加1,則sat為6
一個整數不能直接賦給一個枚舉變量,應先強制類型轉換,例:workday = weekday(2); //C++風格形式
10、共用體(聯合)
11、結構體
12、位段(位域)
枚舉、共用體、結構體、位段源代碼:
復制代碼
#include<iostream>
using namespace std;
//聲明枚舉類型
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};
struct Student //結構體
{
int age;
char name[20];
union P //共用體
{
int grade;
char position[10];
}category;
unsigned a:2;//位段
unsigned b:3;
};
int main()
{
weekday workday;//定義枚舉變量
workday=weekday(1);//C++風格的強制類型轉換格式
cout<<workday<<endl
<<wed<<endl;
Student person[2];//定義共用體變量
person[0].category.grade=20;
cout<<person[0].category.grade<<endl;
person[1].a = 3; //給位段賦值
cout<<"a="<<person[1].a<<endl;
person[1].b = 12;
//十進制12-->二進制1100--取其低3位-->二進制100-->十進制4
cout<<"b="<<person[1].b<<endl;
return 0;
}
復制代碼
13、類
類是對象的抽象,不占存儲空間;對象是類的實例,占用存儲空間
一個對象所占的空間的大小只取決於其中的數據成員所占的空間,而成員函數不占用對象的存儲空間
信息隱蔽 = 類的公用接口與私有實現的分離
把類的聲明(包含成員函數的聲明)放到一個頭文件中;把成員函數的定義放到另一文件中,單獨編譯,從而不重復編譯
類庫 = 類聲明頭文件 + 已編譯過成員函數的定義的目標文件
類的聲明與對象的定義:
成員對象的訪問:
構造函數:
析構函數:
~Box( ){ };
析構函數與類同名,無返回值(類型),無參數
一個類只能有一個析構函數
當對象的生命周期結束時,自動執行析構函數
對一個對象來說,析構函數是最後一個被調用的成員函數
其作用並不是刪除對象,而是在撤銷對象占用的內存之前完成一些清理工作
調用析構函數的順序與構造函數相反
對象指針:
類和對象源代碼(一):
復制代碼
#include<iostream>
using namespace std;
class Box
{
int length, width, height;//私有成員
public:
Box();
//用參數初始化表對數據成員初始化
Box(int len, int w, int h):length(len),width(w),height(h)
{
}
~Box()
{
}
int volume();
};//分號不可少
Box::Box()
{
length = 5;
width = 5;
height = 5;
}
int Box::volume()
{
//顯示的使用this指針
return this->width * this->height * this->length;
}
int main()
{
Box b1;
Box *p = NULL;//指向對象的指針
//定義對象數組
Box b[3] = {Box(),Box(10,10,10),Box(20,20,20)};
p = &b[2];
cout<<b1.volume() <<endl;
cout<<b[0].volume()<<'\t'
<<b[1].volume()<<endl;
cout<<p->volume() <<'\t'
<<(*p).volume()<<endl;
return 0;
}
復制代碼
共用數據的保護:
靜態成員:
對象的賦值與復制:
友元:
類和對象源代碼(二):
復制代碼
#include<iostream>
using namespace std;
class Date;//對Date類的提前引用聲明
class Time
{
public:
Time(int, int, int);
void display(Date &);//形參為對象的引用
private:
int hour,minute,second;
};
class Date
{
public:
Date();
Date(int, int, int);
//聲明Time類中的display函數為Date類的友元成員函數
friend void Time::display(Date &);
private:
int year,month,day;
};
Time::Time(int h, int m, int s):hour(h),minute(m),second(s)
{
}
void Time::display(Date &d)
{
cout<<"d.day="<<d.day<<'\t'
<<"t.hour="<<hour<<endl;
}
Date::Date()
{
year = 1000;
month = 12;
day = 20;
}
Date::Date(int y, int m, int d)
{
year = y;
month = m;
day = d;
}
int main()
{
Time t(5,5,5);
Time t1(t); //對象的復制
Date d(2000,10,30), d1;
d1 = d; //對象的賦值
t.display(d);
t1.display(d1);
return 0;
}
復制代碼
14、運算符重載
實質上是函數重載
形式:函數類型 operator運算符名稱(形參表列){};
C++不允許用戶自己定義新的運算符,只能對已有的C++運算符進行重載
重載不能改變運算符運算對象的個數、優先級別、結合性,不能有默認的參數,參數至少有一個類對象(其引用)
C++約定:若在前置自增(自減)運算符重載函數中增加一個int型形參,就是後置(自減)運算符函數
只能將重載“>>”和“<<”的函數作為友元函數或普通的函數,而不能將他們定義為成員函數
15、繼承和派生
繼承:一個新類從已有的類那裡獲得其已有特性
派生:從已有的類(基類)產生一個新的子類(派生類)
繼承與組合:在一個類中以另一個類的對象作為數據成員
繼承分單繼承和多重繼承,派生分一級派生和多級派生
繼承方式:公用、保護、私有
訪問屬性:公用、保護、私有、不可訪問
派生類的成員:基類的全部成員(不包括構造和析構函數)+自己增加的成員
私有成員只能被本類的成員函數訪問,保護成員可被派生類的成員函數引用
類的成員在不同的作用域中有不同的訪問屬性
派生類的構造與析構函數:
派生類構造函數名(總參數表列):基類構造函數名(參數表列),…,子對象名(參數表列),… { 派生類新增數據成員的初始化語句 }
派生類構造函數的執行順序:基類、子對象、派生類
多重繼承中的初始化是按基類表中的說明順序進行的
析構函數與構造函數相反
若基類未定義構造函數或定義了未帶參數的構造函數,則在派生類中可不寫基類構造函數;在調用派生類構造函數時系統會自動首先調用基類默認的構造函數
多重繼承引起的二義性問題:
基類與派生類的轉換:
繼承與派生源代碼:
復制代碼
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string nam,int a,char s):name(nam),age(a),sex(s)
{
}
void show()
{
cout<<"name:" <<'\t'<<name <<endl
<<"age:" <<'\t'<<age <<endl
<<"sex:" <<'\t'<<sex <<endl;
}
protected:
string name;
int age;
char sex;
};
class Teacher:virtual public Person//聲明Person為公用繼承的虛基類
{
public:
//構造函數
Teacher(string nam,int a,char s,string t):Person(nam, a,s),title(t)
{
}
void display()
{
show();
cout<<"title:"<<'\t'<<title<<'\n';
}
protected:
string title;
};
class Student:virtual public Person//聲明Person為公用繼承的虛基類
{
public:
//構造函數
Student(string nam, int a,char s,float sco):Person(nam,a,s),score(sco)
{
}
void display()
{
show();
cout<<"score:"<<'\t'<<score<<'\n';
}
protected:
float score;
};
class Graduate:public Teacher,public Student//聲明多重繼承的派生類Graduate
{
public:
//不僅要對直接基類初始化,還要對虛基類初始化
Graduate(string nam,int a,char s,string t,float sco,float w):
Person(nam,a,s),Teacher(nam,a,s,t),Student(nam,a,s,sco),wage(w)
{
}
void present()
{
cout<<"name:" <<'\t'<<name <<'\n'
<<"age:" <<'\t'<<age <<'\n'
<<"sex:" <<'\t'<<sex <<'\n'
<<"title:"<<'\t'<<title<<'\n'
<<"score:"<<'\t'<<score<<'\n'
<<"wage:" <<'\t'<<wage <<'\n';
}
private:
float wage;
};
int main()
{
Graduate grad1("WangLi",24,'f',"assistant",99.5,1325.5);
grad1.present();
return 0;
}
復制代碼
16、多態性和虛函數
多態性:用一個函數名調用具有不同內容(功能)的函數;不同對象對同一消息有不同的響應方式(一個接口,多種方法)
從系統實現的角度,多態性分為:靜態多態性(編譯時的多態性)和動態多態性(運行時的多態性,通過虛函數實現)
關聯:調用具體的對象的過程(把一個函數名與一個對象捆綁在一起)
靜態(早期)關聯:通過對象名調用虛函數,在編譯時即可確定其調用的函數屬於哪個類
動態(滯後)關聯:通過基類指針調用虛函數,在運行階段把虛函數和類對象捆綁在一起
虛函數:
類外定義虛函數時不必加virtual
派生類中定義的同名函數:函數名、類型、參數個數、參數類型須與虛函數相同;當一個成員函數被聲明為虛函數後,其派生類的同名函數都自動成為虛函數
先用基類對象指針指向同一類族中某一對象,即可調用該對象中的同名函數
函數重載處理的是同一層次上的同名函數問題,而虛函數處理的是不同派生層次上的同名函數問題(函數覆蓋)
當一個類有虛函數時,編譯系統會為該類構造一個虛函數表(一個指針數組),存放每個虛函數的入口地址
構造函數不能聲明為虛函數;若基類的析構函數聲明為虛函數,則其所有派生類的析構函數均為虛函數
純虛函數與抽象類:
純虛函數:virtual 函數類型 函數名(參數表列)=0;//聲明語句
無函數體;“=0”並不表示返回值為0,只是形式;不具備函數的功能,不能被調用
若在派生類中未對該函數定義,則在派生類中仍為純虛函數
抽象類:不用來定義對象,只作為一種基本類型用作繼承的類
可定義指向抽象類對象的指針變量,通過指針調用具體派生類的虛函數
凡是包含純虛函數的類都是抽象類,抽象基類是本類族的公共接口
多態性與虛函數源代碼:
復制代碼
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
Student(string nam,int a,char s,float sco):name(nam),age(a),sex(s),score(sco)
{
}
// virtual void display() = 0;//純虛函數
virtual void display()//虛函數
{
cout<<"name:" <<'\t'<<name <<'\n'
<<"age:" <<'\t'<<age <<'\n'
<<"sex:" <<'\t'<<sex <<'\n'
<<"score:"<<'\t'<<score<<'\n';
}
protected:
string name;
int age;
char sex;
float score;
};
class Graduate:public Student
{
public:
Graduate(string nam,int a,char s,float sco,float w):
Student(nam,a,s,sco),wage(w)
{
}
void display()//虛函數
{
cout<<"name:" <<'\t'<<name <<'\n'
<<"age:" <<'\t'<<age <<'\n'
<<"sex:" <<'\t'<<sex <<'\n'
<<"score:"<<'\t'<<score<<'\n'
<<"wage:" <<'\t'<<wage <<'\n';
}
private:
float wage;
};
int main()
{
Student stud("LiGang",18,'m',95);
Graduate grad("WangLi",24,'f',99.5,1325.5);
grad.display();//靜態關聯
cout<<"******************************************\n";
Student *pt = &stud;
pt->display();//動態關聯
cout<<"******************************************\n";
pt = &grad;
pt->display();
return 0;
}
復制代碼
17、輸入輸出流
標准I/O:鍵盤輸入數據,顯示到屏幕上
文件I/O:以外存磁盤文件為輸入輸出對象
字符串I/O:對內存中指定的空間(通常為字符數組,利用該空間可存儲任何信息)進行輸入輸出
標准輸入輸出:
標准輸出流
cout,cerr,clog流
格式輸出
使用控制符控制輸出格式:頭文件iomanip
使用流對象的成員函數:格式標志在類ios中被定義為枚舉值
用流成員函數put輸出字符
標准輸入流
cin流
用流成員函數輸入字符:無參、1個、3個參數的get,getline
Istream類的其他成員函數
18、用typedef聲明類型
19、預處理命令
typedef與預處理命令源代碼:
復制代碼
#include<iostream>
using namespace std;
typedef int ARR[2]; //用typedef聲明數組類型
#define MAX(x,y) x>y?x:y //帶參數的宏定義
int main()
{
int max;
ARR a;
a[0]=1;
a[1]=2;
max=MAX(a[0],a[1]);
#ifdef MAX
cout<<"max="<<max<<endl;
#else
cout<<"MAX(x,y) is not defined! "<<endl;
#endif
return 0;