C++拷貝構造的詳細闡述

一、拷貝構造的定義

拷貝構造是一種特殊的構造函數,它是用來初始化同一類中的另一個對象的;其實就是將一個對象中的數據,拷貝到另一個對象中。


class Point{
    public:
        Point(int a,int b) {
            x = a;
            y = b;
        }
        // 拷貝構造函數
        Point(const Point &p) {
            x = p.x;
            y = p.y;
        }
        int x;
        int y;
};

二、拷貝構造的默認實現

如果不顯示定義拷貝構造函數,編譯器會默認提供一個拷貝構造函數,其實現方式是逐個成員進行複製。


class Point{
    public:
        Point(int a,int b) {
            x = a;
            y = b;
        }
        // 拷貝構造函數(默認實現)
        Point(const Point &p) {
            x = p.x;
            y = p.y;
        }
        int x;
        int y;
};

三、防止拷貝的實現方式

有時候,我們需要限制對象的拷貝行為,可以採用以下兩種方式。

1. 聲明拷貝構造函數為私有(禁用拷貝)

將拷貝構造函數聲明為私有的可以禁用對象的拷貝行為,防止對象被複制。


class SingleObject{
    public:
        static SingleObject* getInstance() {
            if (m_instance == nullptr) {
                m_instance = new SingleObject();
            }
            return m_instance;
        }
    private:
        // 禁止拷貝行為
        SingleObject(const SingleObject&);
        SingleObject();
        static SingleObject* m_instance;
};    

2. 將拷貝構造函數定義為delete

C++11標準提供了delete關鍵字,用於防止使用某個函數或者操作符,可以將其置為刪除狀態。


class SingleObject{
    public:
        static SingleObject* getInstance() {
            if (m_instance == nullptr) {
                m_instance = new SingleObject();
            }
            return m_instance;
        }
        // 禁止拷貝行為
        SingleObject(const SingleObject&) = delete;
        SingleObject& operator=(const SingleObject&) = delete;
    private:
        SingleObject();
        static SingleObject* m_instance;
};

四、拷貝構造的調用時機

拷貝構造函數在以下幾種情況下會被調用。

1. 用一個對象去初始化另一個同類對象


class Point{
    public:
        Point(int a,int b) {
            x = a;
            y = b;
        }
        // 拷貝構造函數
        Point(const Point &p) {
            x = p.x;
            y = p.y;
        }
        int x;
        int y;
};
Point p1(1,2);
Point p2 = p1;  // 通過p1去初始化p2,會調用拷貝構造函數

2. 函數傳參時,按值傳遞


class Point{
    public:
        Point(int a,int b) {
            x = a;
            y = b;
        }
        // 拷貝構造函數
        Point(const Point &p) {
            x = p.x;
            y = p.y;
        }
        int x;
        int y;
};
void print(Point p) {}  // 按值傳遞的方式調用函數
Point p(1,2);
print(p);  // 調用函數時按值傳遞,會調用拷貝構造函數

3. 函數返回對象時,按值返回


class Point{
    public:
        Point(int a,int b) {
            x = a;
            y = b;
        }
        // 拷貝構造函數
        Point(const Point &p) {
            x = p.x;
            y = p.y;
        }
        int x;
        int y;
};
Point getPoint() {
    Point p(1,2);
    return p;  // 返回對象時按值返回,會調用拷貝構造函數
}

五、深拷貝和淺拷貝

當類中有指針類型成員時,拷貝構造將會帶來深拷貝和淺拷貝的問題。

1. 深拷貝

拷貝構造時,為指針成員重新申請內存,並將原來內存中的數據複製到新內存中,這樣兩個指針成員指向的內存是不同的。


class String{
public:
    String() {
        m_data = new char[1];
        *m_data = '\0';
    }
    String(const char* data) {
        m_data = new char[strlen(data) + 1];
        strcpy(m_data, data);
    }
    String(const String& other) {
        m_data = new char[strlen(other.m_data) + 1];
        strcpy(m_data, other.m_data);
    }
    ~String() {
        delete[] m_data;
    }
private:
    char* m_data;
};

2. 淺拷貝

拷貝構造時,直接將指針成員的值複製到新對象中,這樣兩個指針成員指向的內存是相同的,會帶來指針重複刪除的問題。


class String{
public:
    String() {
        m_data = new char[1];
        *m_data = '\0';
    }
    String(const char* data) {
        m_data = new char[strlen(data) + 1];
        strcpy(m_data, data);
    }
    String(const String& other) {
        m_data = other.m_data;  // 淺拷貝
    }
    ~String() {
        delete[] m_data;  // 重複刪除指針會導致錯誤
    }
private:
    char* m_data;
};

六、拷貝賦值運算符重載

拷貝賦值運算符(=)和拷貝構造函數的作用很相似,都是進行對象的拷貝,但兩者的調用時機不同。拷貝構造在對象創建時被調用,拷貝賦值在對象創建後被調用。


class String{
public:
    String() {
        m_data = new char[1];
        *m_data = '\0';
    }
    String(const char* data) {
        m_data = new char[strlen(data) + 1];
        strcpy(m_data, data);
    }
    String(const String& other) {
        m_data = new char[strlen(other.m_data) + 1];
        strcpy(m_data, other.m_data);
    }
    ~String() {
        delete[] m_data;
    }
    // 拷貝賦值運算符重載
    String& operator=(const String& other) {
        if (this != &other) {  // 防止自我賦值
            delete[] m_data;
            m_data = new char[strlen(other.m_data) + 1];
            strcpy(m_data, other.m_data);
        }
        return *this;
    }
private:
    char* m_data;
};

七、小結

拷貝構造是一種特殊的構造函數,其作用是用於初始化同一類中的另一個對象。對於指針類型成員,需要注意深拷貝和淺拷貝的問題,避免指針重複刪除。

原創文章,作者:BMAS,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/146224.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
BMAS的頭像BMAS
上一篇 2024-10-29 18:59
下一篇 2024-10-29 18:59

相關推薦

  • index.html怎麼打開 – 詳細解析

    一、index.html怎麼打開看 1、如果你已經擁有了index.html文件,那麼你可以直接使用任何一個現代瀏覽器打開index.html文件,比如Google Chrome、…

    編程 2025-04-25
  • Resetful API的詳細闡述

    一、Resetful API簡介 Resetful(REpresentational State Transfer)是一種基於HTTP協議的Web API設計風格,它是一種輕量級的…

    編程 2025-04-25
  • 關鍵路徑的詳細闡述

    關鍵路徑是項目管理中非常重要的一個概念,它通常指的是項目中最長的一條路徑,它決定了整個項目的完成時間。在這篇文章中,我們將從多個方面對關鍵路徑做詳細的闡述。 一、概念 關鍵路徑是指…

    編程 2025-04-25
  • neo4j菜鳥教程詳細闡述

    一、neo4j介紹 neo4j是一種圖形數據庫,以實現高效的圖操作為設計目標。neo4j使用圖形模型來存儲數據,數據的表述方式類似於實際世界中的網絡。neo4j具有高效的讀和寫操作…

    編程 2025-04-25
  • AXI DMA的詳細闡述

    一、AXI DMA概述 AXI DMA是指Advanced eXtensible Interface Direct Memory Access,是Xilinx公司提供的基於AMBA…

    編程 2025-04-25
  • c++ explicit的詳細闡述

    一、explicit的作用 在C++中,explicit關鍵字可以在構造函數聲明前加上,防止編譯器進行自動類型轉換,強制要求調用者必須強制類型轉換才能調用該函數,避免了將一個參數類…

    編程 2025-04-25
  • HTMLButton屬性及其詳細闡述

    一、button屬性介紹 button屬性是HTML5新增的屬性,表示指定文本框擁有可供點擊的按鈕。該屬性包括以下幾個取值: 按鈕文本 提交 重置 其中,type屬性表示按鈕類型,…

    編程 2025-04-25
  • crontab測試的詳細闡述

    一、crontab的概念 1、crontab是什麼:crontab是linux操作系統中實現定時任務的程序,它能夠定時執行與系統預設時間相符的指定任務。 2、crontab的使用場…

    編程 2025-04-25
  • Vim使用教程詳細指南

    一、Vim使用教程 Vim是一個高度可定製的文本編輯器,可以在Linux,Mac和Windows等不同的平台上運行。它具有快速移動,複製,粘貼,查找和替換等強大功能,尤其在面對大型…

    編程 2025-04-25
  • 網站測試工具的詳細闡述

    一、測試工具的概述 在軟件開發的過程中,測試工具是一個非常重要的環節。測試工具可以快速、有效地檢測軟件中的缺陷,提高軟件的質量和穩定性。與此同時,測試工具還可以提高軟件開發的效率,…

    編程 2025-04-25

發表回復

登錄後才能評論