【C++】拷贝,赋值与构造
拷貝,賦值與構造
文章目錄
- 拷貝,賦值與構造
- 1. 拷貝構造函數/合成拷貝構造函數(copy constructor)
- 2. 拷貝賦值運算符
- 3. 析構函數
1. 拷貝構造函數/合成拷貝構造函數(copy constructor)
1.1 定義:復制構造函數是一個構造函數,它的第一個參數是對類類型的引用,任何附加參數都有默認值。
1.2 如下情況使用:
- case 1:使用 = 定義變量
- case 2: 將對象作為參數傳遞給非引用類型的參數
- case 3: 從具有非引用返回類型的函數返回一個對象
- case 4: 括號初始化數組中的元素或聚合類的成員
- case 5:一些類類型還為它們分配的對象使用復制初始化。
1.3 形如下:
拷貝構造函數的第一個參數必須是類的引用類型,拷貝構造函數在幾種情況下P265會被隱式引用,因此,通常不應該不應該是 explicit 的。
class Foo{public: Foo();Foo(const Foo& obj);//拷貝構造函數
}
1.4 解釋Sales_data::Sales_data(Sales_data rhs);
拷貝構造函數用來初始化非引用類類型參數,這一特性解釋為什么拷貝構造函數自己的參數必須是引用類型。
如上,如果這樣聲明,調用將永遠不會成功調用復制構造函數,Sales_data rhs 是實際的參數(實參),因此,我們需要使用復制構造函數來復制參數,但是要復制參數,我們又將需要調用復制構造函數,如此無限期地陷入死循環,依此類推。
1.5 即使我們自己定義拷貝構造函數,編譯器也會為我們合成一個拷貝構造函數(synthesized copy constructor),對某些類來說,合成拷貝構造函數是用來阻止我們拷貝該類類型的對象。一般情況,合成拷貝構造函數會將其參數成員逐個拷貝到正在創建的對象中,編譯器從給定對象中依次將每個非靜態成員拷貝到正在創建的對象中。
1.6 ** What happens when we copy a StrBlob? What about StrBlobPtrs? **
// added a public member function to StrBlob and StrBlobPrts
long count() {return data.use_count(); // and wptr.use_count();
}// test codes in main()
StrBlob str({ "hello", "world" });
std::cout << "before: " << str.count() << std::endl; // 1
StrBlob str_cp(str);
std::cout << "after: " << str.count() << std::endl; // 2ConstStrBlobPtr p(str);
std::cout << "before: " << p.count() << std::endl; // 2
ConstStrBlobPtr p_cp(p);
std::cout << "after: " << p.count() << std::endl; // 2
當我們復制一個 StrBlob 時,shared_ptr 成員的 use_count 加一。
當我們復制一個 StrBlobPrts 時,weak_ptr 成員的 use_count 不會改變。(因為計數屬于 shared_ptr)
1.7 假設 Point 是具有公共復制構造函數的類類型,請確定此程序片段中復制構造函數的每次使用:
Point global;
Point foo_bar(Point arg) // 1
{Point local = arg, *heap = new Point(global); // 2, 3*heap = local;Point pa[ 4 ] = { local, *heap }; // 4, 5return *heap; // 6
}
1.8 ex13_05_h,給定以下類,編寫一個復制所有成員的復制構造函數。新增構造函數應該動態分配一個新字符串并復制 ps 指向的對象,而不是復制 ps 本身。
class HasPtr {
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
private:std::string *ps;int i;
};
#include <string>class HasPtr {
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
private:std::string *ps;int i;
};
2. 拷貝賦值運算符
2.1 重載運算符(overloaded operator),本質上是一個函數,由 operator + 自定義運算符組成
2.2 拷貝賦值運算符是什么?(復制賦值運算符是名為 operator= 的函數,它采用與類相同類型的參數。)
什么時候用?(發生賦值行為時候)
合成拷貝賦值運算符完成什么工作?(合成的復制賦值運算符,它會將使用該成員類型的復制賦值運算符的右側對象的每個非靜態成員分配給左側對象的相應成員。)
什么時候會生成合成拷貝賦值運算符?(當類沒有定義自己的類時,它會被合成。)
2.3 例子如下
class Foo{public: Foo();Foo& operator=(const Foo& obj);//賦值運算符
}
2.4 合成拷貝賦值運算符用來禁止該對象的賦值P450。
3. 析構函數
3.1 析構函數是什么?合成析構函數完成什么任務?什么時候生成合成析構函數?
析構函數是一個成員函數,類名前面加一個波浪號(~)。
類似拷貝構造函數和拷貝賦值操作運算符一樣,對于某些類,合成析構函數被用來阻止該類型的對象被銷毀。
編譯器為任何沒有定義自己的析構函數的類定義合成析構函數。
析構函數:在一個構造函數中**,成員的初始化是在一個函數體執行之前**完成,且按照他們在類中出現的順序進行初始化。在析構函數中,首先執行函數體,然后銷毀成員,成員的銷毀順序按照初始化的逆序進行。
在對象最后依次使用后,析構函數的函數體可以執行類設計者希望執行的任何收尾工作,通常,析構函數釋放對象在生存期分配的所有資源。
3.1.1 什么時候調用析構函數?
無論何時一個對象被銷毀,都會自動調用其析構函數
- 變量離開其作用域
- 當一個對象被銷毀時,其成員被銷毀
- 容器(stl vector等)被銷毀時,其元素自動被銷毀。
- 堆與動態分配的對象,當調用delete運算符刪除其對應的指針時,其元素被銷毀
- 堆與臨時對象,當創建它的完整表達式結束時被銷毀
3.2 當一個StrBlob對象被銷毀時會發生什么? StrBlobPtr呢?
當銷毀StrBlob對象時,動態對象的use_count將會減少(1)。如果沒有shared_ptr指向該動態對象,則釋放它
當一個StrBlobPter對象被銷毀時,動態分配的對象將不會被釋放。
3.3 Add a destructor to your HasPtr class from the previous exercises. ex13_11.h
#include <string>class HasPtr {
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }HasPtr& operator=(const HasPtr &hp) {std::string *new_ps = new std::string(*hp.ps);delete ps;ps = new_ps;i = hp.i;return *this;}~HasPtr() { delete ps; } //deconstructor
private:std::string *ps;int i;
};
總結
以上是生活随笔為你收集整理的【C++】拷贝,赋值与构造的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求助,求推荐合适的粉底!!楼主狂爱出汗!
- 下一篇: 【C++】拷贝控制与资源管理