c++學習第一篇
1、物件和類概念
物件:實體,真實存在的個體。
編譯器為之分配空間的變數。
特徵屬性,行為。
類: 具有共性的實體的抽象。
自定義的資料型別
struct stu
{
int age;
};
struct stu xiaoming;
xiaoming就是物件,struct stu就是類。
2、C++對於結構的增強
struct stu
{
char *name; //資料
int age;
void fun()
{
printf("age\n",age);
}
};
a.結構名可以直接做型別名。
C:struct stu xiaoming;
C++:stu xiaoming;
b.結構中可以定義函式。
c.結構中對於資料和函式有許可權的劃分。(封裝性)
public(公共的):類內和類外均可以訪問
private(私有的):只能在類內訪問,類外無法訪問。
protected(受保護的):只要不涉及到繼承,和private相同。
補充:
1.struct關鍵字定義的型別中如果成員前沒有許可權符修飾,預設為public
2.許可權符生效範圍是上個許可權符到下個許可權符中間的部分。
3.許可權符可以出現任意次,但不建議這樣使用。
注意:
1、C++的結構除了上述增強外,其餘基本和C中沒有差別,
如:成員訪問方法,對齊原則等均一樣。
2、類的成員函式應當在外部定義,內部給出宣告即可。外部定義時要指定函式的作用域。
C++使用class關鍵字用於定義類,和struct關鍵字的用法相同。只不過class關鍵字預設的型別為private
3、物件的引用
a.同一類的物件可以相互賦值。
當類中有指標且有動態記憶體分配時,不要隨便賦值,有可能會出現問題。
4.建構函式
建構函式主要用於在定義物件時,完成物件的初始化.
每一個類都應該有一個建構函式,如果使用者沒有定義建構函式,
編譯器會自動生成建構函式(引數和函式體為空的建構函式),如果使用者自定義了建構函式,那麼編譯器不再提供預設的建構函式。
特性:
1.建構函式的名稱必須要與當前類的名稱相同。
2.建構函式僅在定義物件時由系統呼叫,其他時間無法呼叫。
3.建構函式可以有引數,也可以沒有引數,但是不允許有返回值。
4.建構函式只能定義為公有成員,不能定義為其他。
呼叫條件:
1.定義物件時。
2.為物件分配動態記憶體時。
3.定義無名物件(稍作了解)
注意:
建構函式可以進行過載,以便用於不同形式的物件的定義。
建構函式還可以使用預設的預設引數。如果建構函式既有過載,又有預設引數時,注意不要產生二義性。
5.解構函式
解構函式是一種特殊的成員函式,完成與建構函式相反的工作,物件退出生命週期時,完成清理的工作。如:釋放記憶體等。
特性:
1.解構函式的名稱與類的名稱相同。為了區分,解構函式名字前面有~
構造:stu(){}
析構:~stu(){}
2.解構函式無參、無返回值。
3.解構函式不可過載。每一個類有且只有一個解構函式,但是可以有多個建構函式。
4.在物件退出生命週期時,編譯器會自動呼叫解構函式。但是,可以人為呼叫解構函式,不過沒意義。
5.一般情況下,使用系統預設的解構函式就可以。當類中有動態記憶體分配時,需要增加自定義的解構函式,否則有可能會導致記憶體洩露。
呼叫條件:
1.物件退出生命週期時。
2.釋放動態分配的物件空間。
6.析構順序
同一作用域下,先構造的後析構。
7.拷貝建構函式
在定義物件時,使用已知物件初始化新的物件。
特性:
1、拷貝建構函式也是建構函式,名稱為類的名字,無返回值。
2、如果沒有自定義的拷貝建構函式,系統會提供預設的拷貝建構函式
stu(const 當前類的物件的引用)
{
}
當類中有指標且進行動態記憶體分配時,要使用自定義的拷貝建構函式。
3、可以自定拷貝建構函式,如果自定義後,預設的拷貝建構函式失效。
補充:
淺拷貝:只複製資料,沒複製記憶體空間。
深拷貝:既拷貝資料,也要複製記憶體空間。
呼叫條件:
1、使用已知物件初始化新物件。
stu s2 = s1;
2、如果函式引數是某類的物件
void fun(stu s1){}
fun(s2);
3、如果函式返回值為某類的物件時。
stu fun(){}
stu s1 = fun();
如果使用無名物件初始化新的物件,不會呼叫拷貝建構函式。
8、const修飾的成員函式
對於一般的成員函式來說,可以訪問和修改任意資料成員的值。
如果不希望某個成員函式修改成員的值可以使用const修飾該函式。
格式:
返回值型別 函式名(引數列表)const
void stu::getnum()const
{
}
如果希望const修飾的成員函式還可以改變某個成員值,可以在該成員前使用mutable修飾。
例子:
C++拷貝建構函式(深拷貝,淺拷貝)
對於普通型別的物件來說,它們之間的複製是很簡單的,例如:
int a=88;
int b=a;
而類物件與普通物件不同,類物件內部結構一般較為複雜,存在各種成員變數。下面看一個類物件拷貝的簡單例子。
#include <iostream>
using namespace std;
class CExample {
private:
int a;
public:
CExample(int b)
{ a=b;}
void Show ()
{
cout<<a<<endl;
}
};
int main()
{
CExample A(100);
CExample B=A;
B.Show ();
return 0;
}
執行程式,螢幕輸出100。從以上程式碼的執行結果可以看出,系統為物件B分配了記憶體並完成了與物件A的複製過程。就類物件而言,相同型別的類物件是通過拷貝建構函式來完成整個複製過程的。下面舉例說明拷貝建構函式的工作過程。
#include <iostream>
using namespace std;
class CExample {
private:
int a;
public:
CExample(int b)
{ a=b;}
CExample(const CExample& C)
{
a=C.a;
}
void Show ()
{
cout<<a<<endl;
}
};
int main()
{
CExample A(100);
CExample B=A;
B.Show ();
return 0;
}
CExample(const CExample& C)就是我們自定義的拷貝建構函式。可見,拷貝建構函式是一種特殊的建構函式,函式的名稱必須和類名稱一致,它的唯一的一個引數是本型別的一個引用變數,該引數是const型別,不可變的。例如:類X的拷貝建構函式的形式為X(X& x)。
當用一個已初始化過了的自定義類型別物件去初始化另一個新構造的物件的時候,拷貝建構函式就會被自動呼叫。也就是說,當類的物件需要拷貝時,拷貝建構函式將會被呼叫。以下情況都會呼叫拷貝建構函式:
一個物件以值傳遞的方式傳入函式體
一個物件以值傳遞的方式從函式返回
一個物件需要通過另外一個物件進行初始化。
如果在類中沒有顯式地宣告一個拷貝建構函式,那麼,編譯器將會自動生成一個預設的拷貝建構函式,該建構函式完成物件之間的位拷貝。位拷貝又稱淺拷貝,後面將進行說明。
自定義拷貝建構函式是一種良好的程式設計風格,它可以阻止編譯器形成預設的拷貝建構函式,提高原始碼效率。
淺拷貝和深拷貝
在某些狀況下,類內成員變數需要動態開闢堆記憶體,如果實行位拷貝,也就是把物件裡的值完全複製給另一個物件,如A=B。這時,如果B中有一個成員變數指標已經申請了記憶體,那A中的那個成員變數也指向同一塊記憶體。這就出現了問題:當B把記憶體釋放了(如:析構),這時A內的指標就是野指標了,出現執行錯誤。
深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的物件發生複製過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。
#include <iostream>
using namespace std;
class CA
{
public:
CA(int b,char* cstr)
{
a=b;
str=new char[b];
strcpy(str,cstr);
}
CA(const CA& C)
{
a=C.a;
str=new char[a]; //深拷貝
if(str!=0)
strcpy(str,C.str);
}
void Show()
{
cout<<str<<endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
};
int main()
{
CA A(10,"Hello!");
CA B=A;
B.Show();
return 0;
}
深拷貝和淺拷貝的定義可以簡單理解成:如果一個類擁有資源(堆,或者是其它系統資源),當這個類的物件發生複製過程的時候,這個過程就可以叫做深拷貝,反之物件存在資源,但複製過程並未複製資源的情況視為淺拷貝。
淺拷貝資源後在釋放資源的時候會產生資源歸屬不清的情況導致程式執行出錯。
Test(Test &c_t)是自定義的拷貝建構函式,拷貝建構函式的名稱必須與類名稱一致,函式的形式引數是本型別的一個引用變數,且必須是引用。
當用一個已經初始化過了的自定義類型別物件去初始化另一個新構造的物件的時候,拷貝建構函式就會被自動呼叫,如果你沒有自定義拷貝建構函式的時候,系統將會提供給一個預設的拷貝建構函式來完成這個過程,上面程式碼的複製核心語句就是通過Test(Test &c_t)拷貝建構函式內的p1=c_t.p1;語句完成的。