c++構造及拷貝構造、析構函數的複習整理

一、構造函數

1、構造函數定義

構造函數函數名與類名相同,無返回值類型(void也不可以),在創建對象時自動執行。

#include <iostream>
using namespace std;

class Person{
private:
    char *m_name;
    int m_age;

public:
    //聲明構造函數
   Person(char *name, int age);
    //聲明普通成員函數
    void show();
};

//定義構造函數
Person::Person(){
    m_name="小孔";
    m_age =20;
}
Person::Person(char *name, int age){
    m_name = name;
    m_age = age;
}
//定義普通成員函數
void Person::show(){
    cout<<m_name<<"\t"<<m_age<<"\t"<<endl;
}

int main(){
    //創建對象時向構造函數傳參,調用帶參構造
    Person p1("小劉", 15);
    p1.show();
    //創建對象時向構造函數傳參
    Person *p2 = new Person("小王", 16);
    p2 -> show();
    //調用無參構造
    Person p3;
    p3.show();

    return 0;
}

構造函數應爲共有屬性,否則在創建對象時就無法調用該構造函數。

構造函數的作用是給類中的成員賦值,調用該構造函數,只需要在創建對象的同時傳參賦值,就像上例創建的對象p1,p2.只不過在棧上創建對象時,實參位於對象名後面,在堆上創建對象時,實參位於類名後面。

2、函數重載

構造函數是可以重載的,就像上例中的構造函數重載,一個該有形參,一個沒有形參。
構造函數的調用是強制性的,**一旦在類中定義了構造函數,那麼創建對象時就一定要調用,不調用是錯誤的。**如果有多個重載的構造函數,那麼創建對象時提供的實參必須和其中的一個構造函數匹配,這也就意味着,創建對象時只有一個構造函數會被調用。
在調用帶形參的函數時,需要在創建對象時,傳遞實參;而在調用沒有參數的構造時,可以直接創建對象,並且不用加(),這就表示調用的是不帶參數的構造。

3、默認構造函數

在c++類中會存在着一個默認構造函數,如果程序員在類的成員中沒有編寫構造函數,那麼系統會自動生成空的構造函數體,儘管這個函數不執行任何操作。
一個類必須有構造函數,要麼用戶自己定義,要麼編譯器自動生成。
如果用戶自己定義了構造函數,那麼編譯器便不再自動生成構造函數。

4、構造函數初始化列表

#include <iostream>
using namespace std;

class Person{
private:
    char *m_name;
    int m_age;

public:
    Person(char *name, int age);
    void show();
};

//採用初始化列表
Person::Person(char *name, int age): m_name(name), m_age(age) {
   
}
void Person::show(){
    cout<<m_name<<m_age<<endl;
}

int main(){
    Person p1("小王", 15);
    p1.show();
    Person *p2 = new Person("小美",16);
    p2 -> show();

    return 0;
}

初始化列表的作用是對類內的成員變量進行初始化,寫法如上例,是在函數首部與函數體之間添加了一個冒號:,隨後用類內成員(形參)的寫法,表示將形參值傳遞給該類內成員。初始化列表可以用於全部成員變量,也可以只用於部分成員變量,用於部分成員變量時,只需要在:後,寫出所要初始化的變量即可。
成員變量的初始化順序與初始化列表中列出的變量的順序無關,它只與成員變量在類中聲明的順序有關。

初始化列表還有一個作用是給const成員變量賦值,初始化 const 成員變量的唯一方法就是使用初始化列表。

二、拷貝構造

拷貝構造是構造函數的延伸,拷貝構造 是一種特殊的構造函數 ,用自身這種類型來構造自身
用戶未定義拷貝構造,系統默認提供一個隱式的拷貝構造,它會將已存在於對象中的數據成員逐個的拷貝到新創建的對象中(淺拷貝)
拷貝構造:類名 (const 類名& 引用名)
如果類中存在動態申請內存,就必須要重寫拷貝構造,來做深拷貝。不然兩個類中的指針會指向同一個地址,在釋放的時候會造成錯誤。
淺拷貝:如果要拷貝的對象內存在動態申請的內存,那麼淺拷貝之後,新對象的指針指向要拷貝的對象的內存地址。
深拷貝:如果要拷貝的對象內存在動態申請的內存,那麼調用深拷貝構造函數,爲新對象申請內存,並保存數據。

class Student
{
private:
 char* m_name;
public:
 Student(const char* name);
 Student(const Student& stu);
 void show();
 ~Student();
};
Student::Student(const char *name)
{
 cout << "帶參構造" << endl;
     m_name = new char[strlen(name) + 1];
 strcpy(m_name, name);
}
Student::Student(const Student& stu)
{
 cout << "拷貝構造" << endl;
 this->m_name = new char[strlen(stu.m_name) + 1];
 strcpy(this->m_name, stu.m_name);
}
void Student::show()
{
 cout << m_name << endl;
}
Student::~Student()
{
 cout << "析構" << endl;
 if (m_name!=NULL)
 {
 delete[]m_name;
 m_name = NULL;
 }
}
int main()
{
 Student stu1("小王");
 Student stu2(stu1);
 
 stu1.show();
 stu2.show();
 
 system("pause");
 return 0;
}

在存在動態內存申請的時候,需要調用拷貝函數,否則執行析構函數,釋放內存時,前一個指針釋放內存後,後一個指針沒有內存可以釋放,會引發異常。

三、 析構函數

創建對象時系統會自動調用構造函數進行初始化工作,同樣,銷燬對象時系統也會自動調用一個函數來進行清理工作,這個函數就是析構函數。

#include<iostream>
using namespace std;

class student
{
private:
 int m_a;
 int m_b;
public:
 student(int a,int b);
 ~student();
};
student::student(int a, int b): m_a(a), m_b(b){}

student::~student()
{
 cout << m_a << "\t" << m_b << endl;
}

int main()
{
 student stu(1,2);
 
 system("pause");
 return 0;
}

上例可見析構函數的形式:~類名(){}

析構函數沒有參數,不能被重載,一個類只能有一個析構函數。如果用戶沒有定義,編譯器會自動生成一個默認的析構函數。

析構函數的調用條件:
在所有函數之外創建的對象是全局對象,它和全局變量類似,位於內存分區中的全局數據區,程序在整體結束執行時會調用這些對象的析構函數。
在棧區系統開闢的內存,系統會自動釋放並調用析構函數。
在堆區手動開闢的內存,手動delete釋放時,纔會調用析構函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章