概述
Clone函數是一種用于復制的計算機函數。在程序編寫中,除了自定義一個拷貝構造函數來實現對象復制外,還可以實現一個clone函數。這需要借助編譯器實現的一個隱藏拷貝構造函數,這樣的做法,更省心。
中文名 | clone函數 |
外文名 | clone |
所屬學科 | 信息學 |
基本介紹
在C++中,要拷貝一個對象,除了自定義一個拷貝構造函數來實現對象復制外,還可以實現一個clone函數。這需要借助編譯器實現的一個隱藏拷貝構造函數,這樣的做法,更省心。
背后的原理是C++的原型(Prototype)模式:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
Prototype模式提供了一個通過已存在對象進行新對象創建的接口(Clone), Clone實現和具體的語言相關,在C++中通過拷貝構造函數實現。
注意:clone函數是virtual的,無法內聯。
示例代碼:
?
#include "stdafx.h"
#include <iostream>class CA
{
public:int value;CA* clone() const { return new CA( *this );}//僅一個構造函數CA(int a ){value=a;}
};int _tmain(int argc, _TCHAR* argv[])
{CA* objA=new CA(10);CA* objtemp=objA->clone();delete objA;std::cout<<objtemp->value;delete objtemp;return 0;
}
Prototype 模式的應用場景在于,你拿到一個 Base* ,它指向某個 Derived 對象,你想克隆出 Derived 對象,但代碼中不寫出 Derived 的具體類型,因為有很多派生類,這種情況下你用構造函數是搞不定的,就需要Prototype 模式了。
原型模式的作用
1、基本就是你需要從A的實例得到一份與A內容相同,但是又互不干擾的實例的話,就需要使用原型模式。
2、用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。這個其實和C++的拷貝構造函數的作用是相似的(但不相同),實際上就是動態抽取 當前對象運行時的狀態。
Clone方法
關于clone方法
在說明clone方法之前,需要對值傳遞和引用傳遞有個初步的了解
其中需要注意的是第三條
(1)基本數據類型傳值,對形參的修改不會影響實參;
(2)引用類型傳引用,形參和實參指向同一個內存地址(同一個對象),所以對參數的修改會影響到實際的對象;
(3)String, Integer, Double等immutable的類型特殊處理,可以理解為傳值,最后的操作不會修改實參對象。(其本質上還是引用傳遞,只不過這些類型是不可變類,可以理解為傳值)
在對值傳遞和引用傳遞有個初步了解之后,我們開始講解clone方法。
使用clone方法的步驟
1、實現clone的類首先需要繼承Cloneable接口以,此接口是一個標識接口,沒有任何接口方法
2、在類中重寫Object類的clone方法
3、在clone方法中調用super.clone
這樣就可以得到實現clone類的的一個Object對象的復制,但這存在一個問題,如果這個類中還存在引用類型怎么辦?在就要對淺復制和深復制說明一下了。
淺復制和深復制
1、類中的成員皆為基本數據類型,使用淺復制
2、類中的成員有引用類型(此時需注意String, Integer, Double等immutable的類型特殊看待,不當成引用類型),使用深復制
淺復制和深復制的使用依據實際情況而定
淺復制:被復制對象的所有變量都含有與原來對象相同的值,引用變量仍然指向原來的對象
深復制:被復制對象的所有變量都含有與原來對象相同的值引用,引用變量指向被復制對象的新變量
原型模式的優勢
一.為什么不用new直接新建對象,而要用原型模式?
首先,用new新建對象不能獲取當前對象運行時的狀態,其次就算new了新對象,在將當前對象的值復制給新對象,效率也不如原型模式高。
二.為什么不直接使用拷貝構造函數,而要使用原型模式?
原型模式與拷貝構造函數是不同的概念,拷貝構造函數涉及的類是已知的,原型模式涉及的類可以是未知的。
原型模式生成的新對象可能是一個派生類。拷貝構造函數生成的新對象只能是類本身。原型模式是描述了一個通用方法(或概念),它不管是如何實現的,而拷貝構造則是描述了一個具體實現方法。
class base { public : base(); base(base &obj); virtual ~base(); virtual base *clone() { return new base(*this) ; }; }; class derived : public base { public : derived(); derived( derived &); virtual base *clone(){return new derived (*this); } .... }; base *obj1 = new base ; base *obj2 = new derived ;//基類指針指向派生類對象,怎樣用基類指針創建一個新的派生類對象?? 用基類的拷貝構造函數顯然不行。 base *obj3 = obj1 .clone(); base *obj4 = obj12.clone();
適用場景
1.資源優化場景
類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。
2.性能和安全要求的場景
通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。
3.一個對象多個修改者的場景
一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。
缺點
1、配備克隆方法需要對類的功能進行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
2、實現原型模式每個派生類都必須實現 Clone接口。
3、逃避構造函數的約束。
參考資料
- 1??C++ clone()函數的用法???.CSDN
- 2?【Linux內核】Clone函數和Cloneable接口簡要說明?
- 3??IT職場筆記???.博客園
- 4??關于clone()方法???.CSDN
- 5?Pytorch中clone(),copy_(),detach(),.data的辨析與應用??知乎
- 6?java中的clone_51CTO博客