C++拷貝構造函數的調用時機

C++拷貝構造函數的調用時機

一、拷貝構造函數調用的時機

​ 當以拷貝的方式初始化對象時會調用拷貝構造函數,這裏需要注意兩個關鍵點,分別是以拷貝的方式初始化對象

1. 初始化對象

初始化對象是指,爲對象分配內存後第一次向內存中填充數據,這個過程會調用構造函數,對象被創建後必須立即初始化。也就是說只要創建對象就會調用構造函數。

2.初始化和賦值的區別

初始化和賦值都是將數據寫入內存中,從表面看,初始化在很多時候都是以複製的方式來實現的,很容易引起混淆。在定義的同時進行復制叫做初始化,定義完成以後再賦值(不管定義的時候有沒有賦值)就叫做賦值。初始化只能由一次,賦值可以由很多次。具體可以看下面的示例。

int a = 100;  //以賦值的方式初始化
a = 200;  //賦值
a = 300;  //賦值
int b;  //默認初始化
b = 29;  //賦值
b = 39;  //賦值

​ 對於基本的數據類型,我們很少會區分初始化和賦值,即使是混淆了,也不會出現什麼錯誤。但是對於類,他們的區別就非常重要了,因爲初始化時會調用構造函數(以拷貝的方式初始化時會調用拷貝構造函數),而賦值時會調用重載過的賦值運算符。

二、拷貝構造函數和普通構造函數調用的例子

#include<iostream>
#include<string>

using namespace std;

class Student {
public:
    Student(string name = "", int age = 0); //普通構造函數
    Student(const Student &Stu); //拷貝構造函數
    Student& operator=(const Student &Stu); // 重載 = 運算符
    void display();
    

private:
    string m_name;
    int m_age;

};

//普通構造函數
Student::Student(string name , int age )
{
    m_name = name;
    m_age = age;
}

//拷貝構造函數
Student::Student(const Student &Stu)
{
    this->m_name = Stu.m_name;
    this->m_age = Stu.m_age;

    cout << "Copy constructor was called." << endl;
}

// 重載 = 運算符
Student& Student:: operator=(const Student &Stu)
{
    this->m_name = Stu.m_name;
    this->m_age = Stu.m_age;

    cout << "operator=() was called." << endl;

    return *this;
}

void Student::display()
{
    cout << m_age << "  " << m_age << endl;
}


int main()
{
    Student stu1("Xiao Ming", 18);  // 調用普通構造函數
    Student stu2("Xiao Wang", 18);  // 調用普通構造函數

    Student stu3 = stu1;  // 調用拷貝構造函數
    stu3 = stu2;  //調用operator=()

    Student stu4(stu1);   // 調用拷貝構造函數

    Student stu5; // 調用普通構造函數
    stu5 = stu2;  //調用operator=()

    return 0;
}

/*
輸出:
    Copy constructor was called.
    operator=() was called.
    Copy constructor was called.
    operator=() was called.


*/

三、以拷貝的方式初始化對象

1. 初始化對象時會調用構造函數,不同的初始化方式會調用不同的構造函數:
  • 如果用傳遞進來的實參初始化對象,那麼會調用普通的構造函數。
  • 如果用現有對象的數據來初始化對象,就會調用拷貝構造函數,這就是以拷貝的方式初始化對象。
2. 以拷貝的方式來初始化對象的幾種情況:
  1. 將其它對象作爲實參。

    Student stu1("Xiao Ming", 18);  // 普通初始化
    Student stu4(stu1);   // 以拷貝的方式進行初始化
    /* 即使我們不在類中顯示定義拷貝構造函數,這種初始化方式也是有效的,編譯器會生成默認的拷貝構造函數 */
  2. 在創建對象的同時賦值。

    Student stu1("Xiao Ming", 18);  // 普通初始化
    Student stu3 = stu1;  // 以拷貝的方式進行初始化
    /* 這是最常見的一種以拷貝的方式初始化對象的情況 */
  3. 函數的形參爲類類型。

    如果函數的形參爲類類型(對象),那麼調用函數時要將另外一個對象作爲實參傳遞進來賦值給形參,這也是以拷貝的方式初始化形參對象,如下所示。

    void func(Student s){
        //TODO:
    }
    Student stu1("Xiao Ming", 18);  // 普通初始化
    func(stu1);  //以拷貝的方式初始化
    
    /* func() 函數有一個 Student 類型的形參 s,將實參 stu 傳遞給形參 s 就是以拷貝的方式初始化的過程。 */
  4. 函數返回值爲類類型(與編譯器有關不絕對)

    ​ 當函數的返回值爲類類型時,return 語句會返回一個對象,不過爲了防止局部對象被銷燬,也爲了防止通過返回值修改原來的局部對象,編譯器並不會直接返回這個對象,而是根據這個對象先創建出一個臨時對象(匿名對象),再將這個臨時對象返回。而創建臨時對象的過程,就是以拷貝的方式進行的,會調用拷貝構造函數,如下所示。

    Student func(){
       Student stu1("Xiao Ming", 18);  // 普通初始化
        return stu1;
    }
    Student stu = func();
posted @ 2019-02-21 22:41 ay-a 閱讀(...) 評論(...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章