1 構造函數和折構函數
對象的初始化和清理是兩個非常重要的安全問題
一個對象或者變量沒有初始狀態,對其使用後果是未知的;
同樣的,使用完一個對象和變量,沒有及時清理,也會造成一定的安全問題;
C++利用了構造函數和析構函數,解決上述問題;這兩個函數將會被編譯器自動錶用,完成對象初始化和清理工作。對象的初始化和清理工作是編譯器強制我們做的事情,因此如果我們不提供析構函數,編譯器會執行編譯器提供的構造函數和析構函數(空實現)
構造函數:主要作用是創造對象時爲對象的成員屬性賦值,構造函數由編譯器自動調用,無需手動調用。
析構函數:主要作用於對象銷燬前,系統自動調用,執行一些清理工作。
構造函數語法:類名(){}
- 1.構造函數,沒有返回值,也不寫void
- 2.函數名稱與類名相同
- 3.構造函數可以有參數,因此可以發生重載
- 4.程序在調用對象會自動調用構造,無需手動調用,而且只會調用一次;
析構函數:~類名(){}
- 1.析構函數,沒有返回值也不寫void
- 2.函數名稱和類名相同,在名稱前加上符號~
- 3.析構函數不可以有參數,因此不可以發生重載;
- 4.程序在對象銷燬前會自動調用析構,無需手動調用,而且只會調用一次;
//1.構造函數
class Person01
{
public:
Person01()
{
cout << "構造函數的調用" << endl;
}
//2.析構函數
//析構函數不可以有參數,不可以發生重載
//在銷燬前會自動的調用析構函數,且只調用一次
~Person01()
{
cout << "析構函數的調用" << endl;
}
};
void test01()
{
Person01 p;
}
int main()
{
//test01();
system("pause");
return 0;
}
2 構造函數的分類和調用
兩種分類方式:
按參數分爲:有參構造、無參構造;
按照調用分爲: 括號法、顯示法、隱式函數轉換法
注意事項1 調用構造默認函數時候,不要+();
注意事項2: 不用利用拷貝構造函數初始化匿名對象
class Person01
{
public:
Person01()
{
cout << "Person01 無參構造函數的調用" << endl;
}
Person01(int a)
{
age = a;
cout << "Person01 有參構造函數的調用" << endl;
}
//拷貝構造函數
Person01(const Person01 &p)
{
//將傳入的人身上的所有參數都copy過來
age = p.age;
cout << "Person01copy 構造函數的調用" << endl;
}
//2.析構函數
~Person01()
{
cout << "Person01 析構函數的調用" << endl;
}
int age;
};
void test01()
{
// 1 括號法
//Person01 p; //無參函數構造
//Person01 p2(10);//有參函數構造;
//拷貝構造函數
//Person01 p3(p2);
/*cout << "p2 的年齡 = " << p2.age << endl;
cout << "p3 的年齡 = " << p3.age << endl;*/
//注意事項1 調用構造默認函數時候,不要+();
//因爲下面這行代碼,編譯器會認爲是一個函數的聲明,不會認爲在創建對象
//Person01 p1();
//2、顯示法
//注意事項二: 不用利用拷貝構造函數初始化匿名對象
Person01 p1;
Person01 p2 = Person01(10); //有參構造
Person01 p3 = Person01(p2); //拷貝構造
//Person01(p3);//編譯器會認爲Person01(p3) = Person01 p3;對象重定義
//Person01(10);//匿名對象 特點,當前行執行結束之後,系統會立即回收匿名對象;
//隱式轉換法
Person01 p4 = 10; // Person01 p4(10) || Person01 p4 = Person01(10);
}
int main()
{
test01();
system("pause");
return 0;
}
3 拷貝構造函數調用時機
C++中拷貝構造函數調用時機通常有三種
- 使用一個以及創建完成的對象來初始化一個對象
- 值傳遞的方式給函數參數傳遞
- 以值方式返回局部對象
//1.使用一個已經創建完畢的對象來初始化一個新對象
void test02()
{
Person02 p1(20);
Person02 p2(p1);
}
//2.值傳遞的方式給函數參數傳值
void doWork(Person02 p)
{
}
void test03()
{
Person02 p;
doWork(p);
}
//3.值方式返回局部變量
Person02 doWork01()
{
Person02 p1;
cout << (int*)& p1 << endl;
return p1;
}
void test04()
{
Person02 p = doWork01();
cout << (int*)& p << endl;
}
int main()
{
test04();
system("pause");
return 0;
}
4 構造函數調用的規則
默認情況下,C++編譯器至少給一個類添加三個函數
- 默認構造函數(空實現)
- 析構函數(空實現)
- 拷貝構造函數(值拷貝)
構造函數調用規則:
- 如果用戶定義了有參構造函數,C++不會提供無參構造函數,但是會提供拷貝構造函數
- 如果用戶定義了拷貝構造函數,C++不會再提供其他構造函數;
5 深拷貝與淺拷貝
淺拷貝:簡單的賦值拷貝操作 ;淺拷貝帶來的問題是堆區內存的重複釋放;
深拷貝:在堆區重新申請空間進行拷貝操作;
class Person02
{
public:
Person02()
{
cout << "Person 無參構造函數" << endl;
}
Person02(int age ,int height)
{
m_Age = age;
m_Hight = new int(height);
cout << "Person 有參構造函數" << endl;
}
Person02(const Person02 &a)
{
m_Age = a.m_Age;
m_Hight = new int(*a.m_Hight);
cout << "Person 拷貝參構造函數" << endl;
}
~Person02()
{
if (m_Hight != NULL)
{
delete m_Hight;
}
cout << "Person02析構函數的調用" << endl;
}
int m_Age;
int* m_Hight;
};
void test05()
{
Person02 p1(18,160);
cout << "p1.age =" << p1.m_Age << endl;
cout << "p1.m_Height =" << *p1.m_Hight << endl;
Person02 p2(p1);
cout << "p2.age =" << p2.m_Age << endl;
cout << "p2.m_Height =" << *p2.m_Hight << endl;
}
int main()
{
test05();
system("pause");
return 0;
}
總結:如果屬性有在堆區開闢的內存,一定要自己提供拷貝構造函數,防止淺拷貝帶來的問題;
6 初始化列表
作用:
C++提供了初始化列表語法,用來初始化屬性;
語法: 構造函數():屬性1(值),屬性2(值)...{}
class Person03
{
public:
//傳統賦值操作
//Person03(int a, int b, int c)
//{
// m_A = a;
// m_B = b;
// m_C = c;
//}
//初始化列表
Person03(int a,int b,int c) : m_A(a), m_B(b), m_C(c) {
}
int m_A;
int m_B;
int m_C;
};
void test06()
{
Person03 p(30,20,10);
cout << "m_A 的值" << p.m_A << endl;
cout << "m_B 的值" << p.m_B << endl;
cout << "m_C 的值" << p.m_C << endl;
}
int main()
{
test06();
system("pause");
return 0;
}
7 類對象作爲類成員
C++類中的成員可以是另一個類的對象,我們稱之爲對象成員;
例如B類中有 對象A 作爲成員,A爲對象成員;那麼當創建B對象時,A與B的構造和析構的順序是誰先誰後呢?
先構造A後構造B;
當其他類的對象作爲本類的成員,當構造的時候需要先構造成員類對象,再構造類本身;析構的順序與之相反;、
//類對象作爲類成員
class Phone
{
public:
Phone(string name)
{
cout << "Phone 構造函數" << endl;
m_PName = name;
}
string m_PName;
};
class Person04
{
public:
Person04(string name, string pName) : m_Name(name),m_Phone(pName)
{
cout << " Person04 構造函數" << endl;
}
string m_Name;
Phone m_Phone;
};
int main()
{
Person04 p("zhangsan ", "iphone");
cout << p.m_Name << " 拿着 " << p.m_Phone.m_PName << endl;
system("pause");
return 0;
}
8 靜態成員函數
靜態成員就是在成員變量和成員函數前加上關鍵字static ,稱之爲靜態成員;
靜態成員分爲:
- 靜態成員變量
- 所有對象共享一份數據
- 在編譯階段分配內存
- 類內聲明,類外初始化
- 靜態成員函數
- 所有對象共享一個函數
- 靜態成員函數只能訪問靜態成員變量
//靜態成員函數
//所有對象共享一個函數
//靜態成員函數只能訪問靜態成員變量
class person
{
public:
static void func()
{
m_A = 100;//靜態成員函數無法訪問非靜態成員變量,因爲無法區別那個對象的變量
cout << "static void func " << endl;
}
static int m_A;
int m_B;
//靜態成員函數也是有訪問權限的
private:
static void func2()
{
}
};
//兩種方式
void test07()
{
//通過對象訪問
person p;
p.func();
//通過類名訪問,不需要對象
person::func();
}
int main()
{
test07();
system("pause");
return 0;
}