c++知識點整理(上)
阿新 • • 發佈:2019-01-29
好久沒整理筆記了,整理了一份自己的c++學習筆記,為了方便查閱複習某模組知識
c++上部分
概要
- 1、基本知識c++
- 2、面向物件:封裝,繼承,多型
- 3、模版
- 4、STL
- 5、特點:①過程程式設計②物件程式設計③泛型程式設計
基礎知識:
istream ostream 輸入流:流的輸入,輸入的資料
標準輸入 標準輸出 標準錯誤
(鍵盤) (顯示屏)
c stdin stdout stderr
c++ cin cout cerr
std(標準名稱空間)::(作用域運算子)
- 名稱空間:防止命名衝突
- <<流輸出運算子
- cin輸入的時候多個數據預設以空格,tab,enter這些空白字元進行分隔
#include <iostream> //istream ostream
using namespace std;
namespace A
{
int a=3;
}
namespace B
{
int a=5;
}
using namespace A;
//using namespace B;
int main()
{
//cout<<"hello world"<<endl;
//cout<<A::a<<" "<<B::a<<endl;
cout<<a<<endl;
return 0;
}
引用&
int a=5;
int &ra=a;//定義一個引用ra,ra是a的一個別名對引用的改動就是對引用的物件的改動引用能夠引用任何合法的變數
傳引用交換兩個數的例項
#include <iostream>
using namespace std;
void Swap(int &ra,int &rb)
{
int r=rb;
rb=ra;
ra=r;
}
int main()
{
int a=5,b=5;
int &ia=a;
cout<<ia<<endl;
ia=3;
cout<<a<<endl;
Swap(a,b); //int &ra=a,int &b=b引用的方式進行交換
cout<<"a="<<a<<" b="<<b<<endl;
return 0;
}
返回一個引用,返回該變數本身而不是副本,從而提高了效率
#include <iostream>
using namespace std;
int main()
{
int a=10;
const int &ra=a;//不能通過改變已經const 修飾ra,改變a;
a=2;
cout<<ra<<endl;
return 0;
}
行內函數(inline)
行內函數:在函式的定義和宣告前加關鍵字inline,則該函式為行內函數,定義為行內函數的函式,呼叫它的時候,程式的執行流程不能有迴圈語句或者開關語句,負責就是為普通函式處理內聯擴充套件是用來消除函式呼叫時的時間開銷。它通常用於頻繁執行的函式。 一個小記憶體空間的函式非常受益。
注:
- 如果有宣告,在宣告之前加了inline,函式的定義不用加。
- 宣告為行內函數的函式體內不能有迴圈語句或者開關語句,否則就是為普通函式處理。
- 行內函數的函式體語句通常在5行左右。
//行內函數例項
#include <iostream>
using namespace std;
inline int fun(int a)
{
return a*a*a;
}
int main()
{
int i;
for(i=1;i<11;i++)
{
cout<<fun(i)<<endl;
}
return 0;
}
函式的過載
函式的過載,多個函式具有相似的功能,我們可以把這些函式取同一個函式名。前提條件是這些函式在引數列表上有差異
引數列表差異:
- 引數的個數不同fun(int) fun(int, int)
- 引數的型別不同fun(int) fun(char)
- 引數的順序不同fun(int,char) fun(char,int)
三者只要滿足一個,即可用同名函式
在呼叫時,會根據函式的實參去自動尋找最佳匹配的引數列表,找到就呼叫。
尋找最佳匹配的三個順序:
- 嚴格查詢匹配 fun(int)fun(1)
- 查詢能夠進行內部資料型別轉換後匹配的fun(int t)fun(‘m’)
- 查詢強制型別轉換後匹配的fun(int) fun((int)1.23)
//比較四個數的大小(函式過載)
#include <iostream>
using namespace std;
int Max(int n,int m,int k,int j)
{
int t=Max(n,m,k);
return t>j?t:j;
}
int Max(int n,int m,int k)
{
int t=Max(n,m);
return t>k?t:k;
}
int Max(int n,int m)
{
return n>m?n:m;
}
int main()
{
int a=1;
int b=2;
int c=3;
cout<<Max(a,b,c)<<endl;
return 0;
}
#include <iostream>
using namespace std;
void Print(int n)
{
cout<<n*n<<endl;
cout<<"xxx"<<endl;
}
void Print(double n)
{
cout<<n*n<<endl;
cout<<"xxx"<<endl;
}
int main()
{
int a=1;
double b=1.2;
Print(a);
Print(b);
return 0;
}
//拓展:判斷連續輸入的數是否重複
#include <iostream>
using namespace std;
int main()
{
int curval=0,val=0;
if(cin>>curval)
{
int count=1;
while(cin>>val)
{
if(curval==val)
++count;
else
{
cout<<curval<<" occurs "<<count<<" times"<<endl;
curval=val;
count=1;
}
}
cout<<curval<<" occurs "<<count<<" times"<<endl;
}
return 1;
}
//一年當中的第幾天
#include <iostream>
using namespace std;
struct Data
{
int i;
int j;
int k;
}data;
int mon[12]={31,29,31,30,31,30,31,31,30,31,30,31};
int islyear(int year)
{
return (year%4==0 && year%100!=0) || year%400==0;
}
int main()
{
int day=0;
cout<<"year:";
cin>>data.i;
cout<<"mon:";
cin>>data.j;
cout<<"day:";
cin>>data.k;
int n=0;
for(n=0;n<data.j-1;n++)
{
day+=mon[n];
}
day+=data.k;
if(!(data.i%4)&&(data.i%100)||!(data.i%400))
if(data.j>=3)
{
day--;
}
cout<<":"<<day<<endl;
return 0;
}
//十三個人123當中的叛徒
typedef struct link{
int num;
int flag;
struct link *next;
}LINK,* pLINK;
#include <iostream>
using namespace std;
#inlcude <stdio.h>
int main()
{
int i,j=1;
pLINK head=(pLINK)malloc(sizeof(LINK));
for(i=1;i<14;i++,j++)
{
pLINK p=(pLINK)malloc(sizeof(LINK));
p->num=i;
if(j==4)
{
j=1;
}
p->flag=j;
if(head->next==NULL)
{
head->next=p;
p->next=NULL;
continue;
}
pLINK tail=head->next;
for (;tail->next!=NULL; tail=tail->next)
{}
tail->next=p;
p->next=NULL;
}
pLINK tail=head->next;
for(;tail->next!=NULL;tail=tail->next);
tail->next=head->next;
pLINK temp=head->next;
int n;
for(i=j;i<5;i++)
{
if(temp->flag==3)
{
temp=temp->next;
i--;
}
else
{
if(i==4)
{
i=1;
}
temp->flag=i;
if(n==temp->num)
{
cout<<temp->num<<" "<<endl;
return 0;
}
n=temp->num;
temp=temp->next;
}
}
return 0;
}
引數預設值函式
引數的預設值函式:在函式定義或宣告的時候,給引數進行賦值,如果呼叫沒有相應的實參,就取預設值
- 設定預設值的時候,從最左到右邊開始設定,必須是連續的
替換時從最左開始依次替換,沒有替換採取預設值
int i(3),j(5);//int i=3,j=5;
int Add(int a,int b=i+j,int c=i*j)
{
return a+b+c;
}
int main()
{
int x(10);
cout<<Add(x)<<endl;
return 0;
}
封裝
引入:描述一個學生diary score name
struct student
{
//特徵:
private:
int diray;//只有自己可以看
protected:
float score;//只有家人朋友可以看
public:
char *name;//大家都可以看
//行為:
void write()
{
...
}
void test()
{
...
}
};
student stu1;
stu1.diray;
stu1.score;
stu1.name;
類(class)
類:型別,自定義的一個型別,在c++中封裝了特徵(成員資料),和行為(成員函式),使它更直接恰當的描述一個物件
類+物件
怎麼定義一個類:
- 關鍵字:struct
struct 類名{
資料成員;
成員函式;
};
- 關鍵字:class
class A
{
private:
int a;
public:
void Print
{
count<<a<<endl;
}
};
//查詢是不是閏年
class data{
private:
int year;
int month;
int day;
public:
void Setday(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
void Print()
{
cout<<year<<" "<<month<<" "<<day<<endl;
}
int Islyear()
{
return (year%4==0 && year%100!=0)|| year%400==0;
}
};
int main()
{
data d;
d.Setday(2016,1,1);
d.Print();
if(d.Islyear())
{
cout<<"is leap year"<<endl;
}
else
{
cout<<"not leap year"<<endl;
}
return 0;
}
物件
用類定義物件: A a;定義了一個A型別的物件a;
class Spot{
private:
int x;
int y;
public:
void Setspot(int a,int b);
void Mvspot(int a,int b);
void Print();
};
void Spot::Setspot(int a,int b)
{
x=a;
y=b;
}
void Spot::Mvspot(int a,int b)
{
x+=a;
y+=b;
}
void Spot::Print()
{
cout<<"x:"<<x<<" --- y:"<<y<<endl;
}
int main()
{
Spot s;
s.Setspot(0,0);
s.Print();
int a,b;
cin>>a>>b;
s.Mvspot(a,b);
s.Print();
return 0;
}
在一個類裡面,擁有資料成員和成員函式,並且每一個成員都要標明它的被訪問許可權,也就是要用下面關鍵字來限制說明每一個成員
- private:修飾成員,在類的外面不能被訪問
- protected:修飾成員,在類的外面不能被訪問
- public:修飾成員,在任何都能被訪問
class和struct的區別:(面試)
- class定義的類,不寫被訪問許可權預設為私有的private
- struct定義的類,不寫被訪問許可權預設為公有的public
使用類時要注意:
- 每個成員都要有被訪問許可權至於順序隨便
- 可以在類裡面定義成員函式,但是不能在類裡面定義資料成員
- 定義類的時候自身類的物件不能作為該類的資料成員
class A
{
private:
A a;//錯誤
A *p;//正確
};
//模擬取錢系統
class Card{
private:
int a;
int b;
int c;
public:
void setcard(int x,int y,int z)
{
a=x;
b=y;
c=z;
}
void printcard()
{
cout<<"餘額:"<<a<<"元"<<b<<"角"<<c<<"分"<<endl;
}
void depcard(int x,int y,int z)
{
a+=x;
b+=y;
c+=z;
}
void takecard(int x,int y,int z)
{
a-=x;
b-=y;
c-=z;
}
};
int main()
{
Card user;
user.setcard(0,0,0);
while(1)
{
cout<<"1.餘額"<<endl;
cout<<"2.存款"<<endl;
cout<<"3.取款"<<endl;
cout<<"0.EXIT"<<endl;
int key;
cin>>key;
int x,y,z;
switch(key)
{
case 1:
user.printcard();
break;
case 2:
cout<<"請輸入存款(元,角,分):"<<endl;
cin>>x>>y>>z;
user.depcard(x,y,z);
break;
case 3:
cout<<"請輸入取款(元,角,分):"<<endl;
cin>>x>>y>>z;
user.takecard(x,y,z);
break;
case 0:
return 0;
}
}
return 0;
}
成員函式的過載
- 同一個類裡面的成員函式可以發生過載。
- 但是不同類,即使名字相同,也不能過載。
- 通常把類的定義放在標頭檔案中。
class A
{
void print (int i);
void print (int i,int j);
}
類與記憶體
- 類不佔記憶體,它只是一個型別,物件或者變數才佔記憶體。
- 在類裡面佔記憶體的是它的資料成員。
建構函式:
- 作用:給物件開闢空間並初始化。
- 定義:屬於類裡特殊的成員函式,函式名等於類名,沒有返回值,也沒有返回值型別,在定義物件的時候被系統自動呼叫。
- 類裡面存在建構函式的話系統將不會提供預設的建構函式
建構函式的種類:
- 預設構造的函式
①當沒有寫建構函式的時候系統提供一個預設的
②自己寫的不帶引數的建構函式
class A
{
private:
int a,b;
public:
A()
{
a=1;
b=2;
}
void Print()
{
cout<<a<<b<<endl;
}
};
int main()
{
A a;
a.Print();
return 0;
}
- 帶引數的建構函式
因此建構函式也可以發生函式的過載且儘量要寫個不帶引數的建構函式。
A(int i)
A(int i,int j)
class A{
private:
int a,b;
public:
A()
{
cout<<"1using"<<endl;
}
A(int i,int j)
{
a=i;
b=j;
cout<<"2using";
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
};
int main()
{
A a(1,2);
a.Print();
return 0;
}
- 拷貝建構函式/預設拷貝建構函式
A(A &a)
{
}
class A{
private:
int a,b;
public:
A()
{
cout<<"1using"<<endl;
}
A(int i,int j)
{
a=i;
b=j;
cout<<"2using"<<endl;
}
A(A &c)
{
a=c.a;
b=c.b;
cout<<"3using"<<endl;
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
};
int main()
{
A a(1,2);
A b(a); //A b=a;
b.Print();
return 0;
}
深拷貝和淺拷貝:
- 淺拷貝的時候簡單的把值拷貝進去。
- 深拷貝連同資源一塊拷貝
解構函式:
- 作用 :負責釋放物件的空間,也是類的特殊的成員函式,也是由系統自動呼叫,沒有返回值,包括返回值型別,也沒有引數,所以不能發生函式的過載
- 定義:~類名(){}
//例:解構函式
~A()
{
}
- 析構順序:先構造的物件後析構,即解構函式的呼叫順序與構造相反
class A{
private:
int a;
public:
A(){}
A(int j)
{
a=j;
cout<<a<<"use A"<<endl;
}
void Print()
{
cout<<a<<"----"<<endl;
}
~A()
{
cout<<a<<"use ~A"<<endl;
}
};
int main()
{
A a(1);
A b(2);
return 0;
}
類中的static
- 靜態資料成員:
在成員前面間static,靜態資料成員是屬於類的,是所有物件公用的,在使用它之前需要類外初始化。
類名::靜態資料變數名”來應用它
class A{
private:
static float money;
public:
void Save(int a)
{
money+=a;
}
float Show()
{
return money;
}
};
float A::money(10);
int main()
{
A h,w;
h.Save(1000);
w.Save(1000);
cout<<h.Show()<<endl;
return 0;
}
//從右向左運算
class A{
private:
int a,b,c;
static int sum;
public:
A(int i,int j,int k)
{
a=i;
b=j;
c=k;
sum+=a+b+c;
}
void Show()
{
cout<<a<<" "<<b<<" "<<c<<endl;
}
int Resum()
{
return sum+=1;
}
};
int A::sum(0);
int main()
{
A m(1,2,3);
A n(4,5,6);
cout<<n.Resum()<<" "<<m.Resum()<<endl;//計算從右到左
cout<<n.Resum()<<" "<<m.Resum()<<endl;
return 0;
}
- 靜態成員函式:
(1) 定義一個靜態成員函式,在該成員函式裡面分別訪問靜態資料成員和普 通資料成員,探索分別有哪些訪問方式。在類外呼叫該靜態成員函式又有哪些訪問方式。
(2)在靜態函式裡面只能通過”物件.”的方式來訪問普通資料成員,不能直接訪問。訪問靜態資料成員,三種方式都可以。
(3)在類外可以通過物件來訪問靜態成員函式,也可以通過類名作用域來訪問靜態成員函式
class A{
private:
int m;
static int n;
public:
A(int a)
{
m=a;
n=+a;
}
static void fun(A a)
{
cout<<"m= "<<a.m<<endl;// m , A::m , a.m
cout<<"n= "<<n<<A::n<<a.n<<endl;
}
};
int A::n(1);
int main()
{
A a1(1);
A a2(2);
A::fun(a2);
a2.fun(a2);
return 0;
}
靜態成員函式的一個特點:就是專門用來處理靜態資料成員,可以脫離物件的存在而被呼叫
初始化列表:
在一個類裡面有資料成員,常整型數(const int a),靜態常整型數(static const int b),常整型數的引用(const int &c),試在建構函式裡面分別給他們初始化,並呼叫成員函式 Print(),輸出它們的值
常資料成員,引用資料成員的初始化放在建構函式初始化列表裡進行。
class A
{
private:
const int a;
static const int b;
const int &c;
public:
A(int i):a(i),c(a)//建構函式初始化列表
{
}
void Print()
{
cout<<a<<" "<<b<<" "<<c<<endl;
}
};
const int A::b(10);
int main()
{
A a(1);
a.Print();
return 0;
}
在類裡面定義一個普通成員函式Print,和一個同名的常成員函式Print,在主函式裡定義一個該類的普通物件和常物件,分別呼叫Print()函式,觀察實驗現象,得出結論:
- 常物件預設呼叫常成員函式,普通物件預設呼叫同名的普通成員函式
- 普通成員的普通物件既能呼叫普通成員的函式,也能呼叫常成員函式
- 常物件只能呼叫常成員函式
class A{
private:
int a;
const int b;
public:
A(int i,int j):b(j)
{
a=i;
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
void Print() const
{
cout<<a+1<<" "<<b+1<<endl;
}
};
int main()
{
A a(1,2);//1 2
const A b(3,4);//4 5 常物件
a.Print();
b.Print();
return 0;
}
常成員函式裡面可以給資料成員賦值嗎?不能。
常成員函式裡面可以呼叫常成員函式嗎?可以呼叫普通成員函式嗎?前者可以(兩個常成員函式裡都不會改變資料數值),後者不可以。
分配(釋放)記憶體[new(delete)]
int *p=new int;
int *p=new int(5);
class A {
private :
int a;
int b;
char *p;
public:
A (int i,int j,const char *str)
{
a=i;
b=j;
p=new char[20];
strcpy(p,str);
}
A (A &C)
{
a=C.a;
b=C.b;
p=new char[20];
strcpy(p,C.p);
}
~A()
{
delete[] p;
}
void Print()
{
cout<<a<<" "<<b<<" "<<p<<endl;
}
};
int main()
{
A a(1,2,"qwer");
a.Print();
A b(a);
b.Print();//
return 0;
}
分別定義一個指向類裡面的資料成員和函式成員的指標。通過指標訪問相應的資料成員和函式成員
class A
{
private:
int a;
public:
int c;
A(int i)
{
a=i;
}
int fun(int b)
//void fun(); void (A::*pfun)(); void (p->*pfun)();
{
return a*c+b;
}
};
int main()
{
A x(10);
int A::* pc=&A::c;//pc為指向類裡資料成員的指標public
x.*pc=5;
int (A::* pfun)(int)=&A::fun;//pfun為指向類裡成員函式的指標public
A *p=&x;
cout<<(p->*pfun)(20)<<endl;
return 0;
}
友元函式(friend)
- 引入原因和目的:由於類具有封裝性,類裡面的私有成員不能在類外被直接訪問,只能通過類的公共成員函式來訪問,由於頻繁的呼叫成員函式,函式傳參,型別檢查都佔用時間和空間,從而降低了效率,為了提高效率,我們需要在類外的普通函式能夠不用通過呼叫成員函式而能直接訪問私有資料成員,故此,引進友元函式。
- 它能夠直接訪問類裡面的任何成員,包括私有成員,通過物件訪問。友元函式是一個定義在類外面的普通函式,不是類的成員函式,但是在類裡面被說明為類的朋友。
class A{
friend void func(A &c);//宣告這個函式是類的朋友,可放在類裡面任何地方
private:
int a;
int b;
public:
A(int i,int j)
{
a=i;
b=j;
}
void fun()
{
a=10;
b=10;
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
};
void func(A &c)//友元函式通過該類的物件來訪問類的私有成員
{
c.a=1;
c.b=1;
}
int main()
{
A a(11,12);
a.Print();
a.fun();
a.Print();
func(a);
a.Print();
return 0;
}
/******************
* 友元函式例項
******************
/*有一個類Point 在該類裡面有一個獲得點位置的函式Getxy();有一個求兩點之間距離的友元函式,double Distance(Point,Point),
是求主函式中兩點間距離。*/
class Point
{
friend double Distance(Point a,Point b);
private:
int x;
int y;
public:
Point(int i,int j)
{
x=i;
y=j;
}
void Getxy()
{
cout<<x<<" "<<y<<endl;
}
};
double Distance(Point a,Point b)
{
double dx=a.x-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
int main()
{
Point a(1,2);
a.Getxy();
Point b(2,3);
b.Getxy();
cout<<Distance(a,b)<<endl;
return 0;
}
友元類
- 友元類,即y是x的朋友,即y所有成員函式都是x的友元函式
class X{
friend class Y;//友元類,即y是x的朋友,即y所有成員函式都是x的友元函式
private:
int a;
static int b;
public:
void Set(int x)
{
a=x;
}
void Display()
{
cout<<a<<" "<<b<<endl;
}
};
int X::b(10);
class Y{
private:
X a;
public:
void Set(int x,int y)
{
a.a=x;
a.b=y;
}
void Display()
{
cout<<a.a<<" "<<a.b<<endl;
}
};
區域性類:
- 定義一個在函式裡面的類
- 區域性類的成員函式不能夠在類外定義
- 區域性類作用域只限於它的函式體內
- 區域性類不能擁有靜態資料成員
void func()
{
class A{
private:
int a;
public:
A(int i)
{
a=i;
}
void Print()
{
cout<<a<<endl;
}
};
A a(1);
a.Print();
}
int main()
{
func();
return 0;
}
this指標
- 在cpp中當一個成員函式被呼叫的時候,系統會向它傳遞一個預設的引數,這個引數是一個指標,一個指向接收該成員函式呼叫的物件的指標,即指向呼叫該成員函式的物件的指標,叫this指標,這樣就建立起了被調函式與呼叫物件之間的關係了
class A
{
private:
int a,b;
public:
void Set(int i ,int j)
{
this->a=i;
this->b=j;
}
}
class A{
private:
int a,b;
public:
A(int i,int j)
{
a=i;
b=j;
}
void Copy(A &c)
{
if(this==&c)
{
return;
}
*this=c;
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
};
int main()
{
A a1(1,2);
A a2(a1);
a2.Print();
return 0;
}
注:當用一個含有資料成員的類,定義一個常物件時,如果沒有建構函式,將不能成功,除非這個類裡面,沒有資料成員
class A
{
private:
int a;
public:
A()
{};
void Set(int i)
{
a=i;
}
void Print()
{
cout<<a<<endl;
}
};
int main()
{
//A a;
const A a;
//當用一個含有資料成員的類定義一個常物件時
//如果沒有建構函式,將不能成功,除非這個類裡面,沒有資料成員
return 0;
}
無名物件
- 用完就釋放
- 呼叫右邊的建構函式完成左邊物件的構造
class A
{
private:
int a,b;
public:
A()
{
cout<<"defualt"<<endl;
}
A(int i,int j)
{
a=i;
b=j;
cout<<"constructor"<<a<<endl;
}
~A()
{
cout<<"deconstructor"<<a<<endl;
}
void Print()
{
cout<<a<<" "<<b<<endl;
}
};
int main()
{
/*
A a1;
a1=A(1,2);//無名物件,用完就釋放
defualt
constructor1
deconstructor1
deconstructor1
*/
A a2=A(3,4);//呼叫右邊的建構函式完成左邊物件的構造
A a1(1,2);
return 0;
}
class A{
private:
char name[20];
int b;
public:
A()
{
cout<<"default"<<endl;
}
A(const char *str,int n)
{
strcpy(name,str);
b=n;
cout<<"constructor:"<<name<<endl;
}
~A()
{
cout<<"deconstructor:"<<name<<endl;
}
void Get(char *s,int &n)
{
strcpy(s,name);
n=b;
}
};
int main()
{
/*char name[20];
int b;
A *p;
p=new A[5];
p[0]=A("liu",1);
p[1]=A("zhang",2);
p[2]=A("wang",3);
*(p+3)=A("li",4);
*(p+4)=A("sun",5);
for(int i=0;i<5;i++)
{
p[i].Get(name,b);
cout<<name<<" "<<b<<endl;
}*/
A m[2]={