日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++构造函数总结

發(fā)布時間:2024/9/27 c/c++ 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++构造函数总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一.構(gòu)造函數(shù)基本概念(本部分源自C++ 類構(gòu)造函數(shù) & 析構(gòu)函數(shù))

1.1 類的構(gòu)造函數(shù)

1.1.1 概念

類的構(gòu)造函數(shù)是類的一種特殊的成員函數(shù),它會在每次創(chuàng)建類的新對象時執(zhí)行。

構(gòu)造函數(shù)的名稱與類的名稱是完全相同的,并且不會返回任何類型,也不會返回 void。構(gòu)造函數(shù)可用于為某些成員變量設(shè)置初始值。

下面的實例有助于更好地理解構(gòu)造函數(shù)的概念:

#include <iostream>using namespace std;class Line {public:void setLength( double len );double getLength( void );Line(); // 這是構(gòu)造函數(shù)private:double length; };// 成員函數(shù)定義,包括構(gòu)造函數(shù) Line::Line(void) {cout << "Object is being created" << endl; }void Line::setLength( double len ) {length = len; }double Line::getLength( void ) {return length; } // 程序的主函數(shù) int main( ) {Line line;// 設(shè)置長度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0; }

當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:

Object is being created Length of line : 6

1.2 帶參數(shù)的構(gòu)造函數(shù)

默認的構(gòu)造函數(shù)沒有任何參數(shù),但如果需要,構(gòu)造函數(shù)也可以帶有參數(shù)。這樣在創(chuàng)建對象時就會給對象賦初始值,如下面的例子所示:

#include <iostream>using namespace std;class Line {public:void setLength( double len );double getLength( void );Line(double len); // 這是構(gòu)造函數(shù)private:double length; };// 成員函數(shù)定義,包括構(gòu)造函數(shù) Line::Line( double len) {cout << "Object is being created, length = " << len << endl;length = len; }void Line::setLength( double len ) {length = len; }double Line::getLength( void ) {return length; } // 程序的主函數(shù) int main( ) {Line line(10.0);// 獲取默認設(shè)置的長度cout << "Length of line : " << line.getLength() <<endl;// 再次設(shè)置長度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0; }

當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:

Object is being created, length = 10 Length of line : 10 Length of line : 6

1.3 使用初始化列表來初始化字段

使用初始化列表來初始化字段:

Line::Line( double len): length(len) {cout << "Object is being created, length = " << len << endl; }

上面的語法等同于如下語法:

Line::Line( double len) {length = len;cout << "Object is being created, length = " << len << endl; }

?假設(shè)有一個類 C,具有多個字段 X、Y、Z 等需要進行初始化,同理地,您可以使用上面的語法,只需要在不同的字段使用逗號進行分隔,如下所示:

C::C( double a, double b, double c): X(a), Y(b), Z(c) {.... }

1.4 類的析構(gòu)函數(shù)

類的析構(gòu)函數(shù)是類的一種特殊的成員函數(shù),它會在每次刪除所創(chuàng)建的對象時執(zhí)行。

析構(gòu)函數(shù)的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作為前綴,它不會返回任何值,也不能帶有任何參數(shù)。析構(gòu)函數(shù)有助于在跳出程序(比如關(guān)閉文件、釋放內(nèi)存等)前釋放資源。

下面的實例有助于更好地理解析構(gòu)函數(shù)的概念:

#include <iostream>using namespace std;class Line {public:void setLength( double len );double getLength( void );Line(); // 這是構(gòu)造函數(shù)聲明~Line(); // 這是析構(gòu)函數(shù)聲明private:double length; };// 成員函數(shù)定義,包括構(gòu)造函數(shù) Line::Line(void) {cout << "Object is being created" << endl; } Line::~Line(void) {cout << "Object is being deleted" << endl; }void Line::setLength( double len ) {length = len; }double Line::getLength( void ) {return length; } // 程序的主函數(shù) int main( ) {Line line;// 設(shè)置長度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0; }

當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:

Object is being created Length of line : 6 Object is being deleted

二、拷貝構(gòu)造函數(shù)(本部分源自C++拷貝構(gòu)造函數(shù)詳解)

2.1. 什么是拷貝構(gòu)造函數(shù)

首先對于普通類型的對象來說,它們之間的復(fù)制是很簡單的,例如:

int a = 100; int b = a;

而類對象與普通對象不同,類對象內(nèi)部結(jié)構(gòu)一般較為復(fù)雜,存在各種成員變量。
下面看一個類對象拷貝的簡單例子。

#include <iostream> using namespace std;class CExample { private:int a; public://構(gòu)造函數(shù)CExample(int b){ a = b;}//一般函數(shù)void Show (){cout<<a<<endl;} };int main() {CExample A(100);CExample B = A; //注意這里的對象初始化要調(diào)用拷貝構(gòu)造函數(shù),而非賦值B.Show ();return 0; }

運行程序,屏幕輸出100。從以上代碼的運行結(jié)果可以看出,系統(tǒng)為對象 B 分配了內(nèi)存并完成了與對象 A 的復(fù)制過程。就類對象而言,相同類型的類對象是通過拷貝構(gòu)造函數(shù)來完成整個復(fù)制過程的。

下面舉例說明拷貝構(gòu)造函數(shù)的工作過程。

#include <iostream> using namespace std;class CExample { private:int a; public://構(gòu)造函數(shù)CExample(int b){ a = b;}//拷貝構(gòu)造函數(shù)CExample(const CExample& C){a = C.a;}//一般函數(shù)void Show (){cout<<a<<endl;} };int main() {CExample A(100);CExample B = A; // CExample B(A); 也是一樣的B.Show ();return 0; }

CExample(const CExample& C) 就是我們自定義的拷貝構(gòu)造函數(shù)。可見,拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱必須和類名稱一致,它必須的一個參數(shù)是本類型的一個引用變量

2.2 . 拷貝構(gòu)造函數(shù)的調(diào)用時機

在C++中,下面三種對象需要調(diào)用拷貝構(gòu)造函數(shù)!


2.2.1. 對象以值傳遞的方式傳入函數(shù)參數(shù)

class CExample { private:int a;public://構(gòu)造函數(shù)CExample(int b){ a = b;cout<<"creat: "<<a<<endl;}//拷貝構(gòu)造CExample(const CExample& C){a = C.a;cout<<"copy"<<endl;}//析構(gòu)函數(shù)~CExample(){cout<< "delete: "<<a<<endl;}void Show (){cout<<a<<endl;} };//全局函數(shù),傳入的是對象 void g_Fun(CExample C) {cout<<"test"<<endl; }int main() {CExample test(1);//傳入對象g_Fun(test);return 0; }

調(diào)用g_Fun()時,會產(chǎn)生以下幾個重要步驟:
(1).test對象傳入形參時,會先會產(chǎn)生一個臨時變量,就叫 C 吧。
(2).然后調(diào)用拷貝構(gòu)造函數(shù)把test的值給C。 整個這兩個步驟有點像:CExample C(test);
(3).等g_Fun()執(zhí)行完后, 析構(gòu)掉 C 對象。

2.2.2. 對象以值傳遞的方式從函數(shù)返回

class CExample { private:int a;public://構(gòu)造函數(shù)CExample(int b){ a = b;}//拷貝構(gòu)造CExample(const CExample& C){a = C.a;cout<<"copy"<<endl;}void Show (){cout<<a<<endl;} };//全局函數(shù) CExample g_Fun() {CExample temp(0);return temp; }int main() {g_Fun();return 0; }

?當(dāng)g_Fun()函數(shù)執(zhí)行到return時,會產(chǎn)生以下幾個重要步驟:
(1). 先會產(chǎn)生一個臨時變量,就叫XXXX吧。
(2). 然后調(diào)用拷貝構(gòu)造函數(shù)把temp的值給XXXX。整個這兩個步驟有點像:CExample XXXX(temp);
(3). 在函數(shù)執(zhí)行到最后先析構(gòu)temp局部變量。
(4). 等g_Fun()執(zhí)行完后再析構(gòu)掉XXXX對象。

2.2.3. 對象需要通過另一個對象進行初始化;

CExample A(100); CExample B = A; // CExample B(A);

后兩句都會調(diào)用拷貝構(gòu)造函數(shù)。

2.3. 淺拷貝和深拷貝

2.3.1. 默認拷貝構(gòu)造函數(shù)

??? 很多時候在我們都不知道拷貝構(gòu)造函數(shù)的情況下,傳遞對象給函數(shù)參數(shù)或者函數(shù)返回對象都能很好的進行,這是因為編譯器會給我們自動產(chǎn)生一個拷貝構(gòu)造函數(shù),這就是“默認拷貝構(gòu)造函數(shù)”,這個構(gòu)造函數(shù)很簡單,僅僅使用“老對象”的數(shù)據(jù)成員的值對“新對象”的數(shù)據(jù)成員一一進行賦值,它一般具有以下形式:

Rect::Rect(const Rect& r) {width = r.width;height = r.height; }

?當(dāng)然,以上代碼不用我們編寫,編譯器會為我們自動生成。但是如果認為這樣就可以解決對象的復(fù)制問題,那就錯了,讓我們來考慮以下一段代碼:

class Rect { public:Rect() // 構(gòu)造函數(shù),計數(shù)器加1{count++;}~Rect() // 析構(gòu)函數(shù),計數(shù)器減1{count--;}static int getCount() // 返回計數(shù)器的值{return count;} private:int width;int height;static int count; // 一靜態(tài)成員做為計數(shù)器 };int Rect::count = 0; // 初始化計數(shù)器int main() {Rect rect1;cout<<"The count of Rect: "<<Rect::getCount()<<endl;Rect rect2(rect1); // 使用rect1復(fù)制rect2,此時應(yīng)該有兩個對象cout<<"The count of Rect: "<<Rect::getCount()<<endl;return 0; }

?這段代碼對前面的類,加入了一個靜態(tài)成員,目的是進行計數(shù)。在主函數(shù)中,首先創(chuàng)建對象rect1,輸出此時的對象個數(shù),然后使用rect1復(fù)制出對象rect2,再輸出此時的對象個數(shù),按照理解,此時應(yīng)該有兩個對象存在,但實際程序運行時,輸出的都是1,反應(yīng)出只有1個對象。此外,在銷毀對象時,由于會調(diào)用銷毀兩個對象,類的析構(gòu)函數(shù)會調(diào)用兩次,此時的計數(shù)器將變?yōu)樨摂?shù)。

說白了,就是拷貝構(gòu)造函數(shù)沒有處理靜態(tài)數(shù)據(jù)成員。

出現(xiàn)這些問題最根本就在于在復(fù)制對象時,計數(shù)器沒有遞增,我們重新編寫拷貝構(gòu)造函數(shù),如下:

class Rect { public:Rect() // 構(gòu)造函數(shù),計數(shù)器加1{count++;}Rect(const Rect& r) // 拷貝構(gòu)造函數(shù){width = r.width;height = r.height;count++; // 計數(shù)器加1}~Rect() // 析構(gòu)函數(shù),計數(shù)器減1{count--;}static int getCount() // 返回計數(shù)器的值{return count;} private:int width;int height;static int count; // 一靜態(tài)成員做為計數(shù)器 };

2.3.2. 淺拷貝

??? 所謂淺拷貝,指的是在對象復(fù)制時,只對對象中的數(shù)據(jù)成員進行簡單的賦值,默認拷貝構(gòu)造函數(shù)執(zhí)行的也是淺拷貝。大多情況下“淺拷貝”已經(jīng)能很好地工作了,但是一旦對象存在了動態(tài)成員,那么淺拷貝就會出問題了,讓我們考慮如下一段代碼:

class Rect { public:Rect() // 構(gòu)造函數(shù),p指向堆中分配的一空間{p = new int(100);}~Rect() // 析構(gòu)函數(shù),釋放動態(tài)分配的空間{if(p != NULL){delete p;}} private:int width;int height;int *p; // 一指針成員 };int main() {Rect rect1;Rect rect2(rect1); // 復(fù)制對象return 0; }

在這段代碼運行結(jié)束之前,會出現(xiàn)一個運行錯誤。原因就在于在進行對象復(fù)制時,對于動態(tài)分配的內(nèi)容沒有進行正確的操作。我們來分析一下:

??? 在運行定義rect1對象后,由于在構(gòu)造函數(shù)中有一個動態(tài)分配的語句,因此執(zhí)行后的內(nèi)存情況大致如下:

?在使用rect1復(fù)制rect2時,由于執(zhí)行的是淺拷貝,只是將成員的值進行賦值,這時?rect1.p?= rect2.p,也即這兩個指針指向了堆里的同一個空間,如下圖所示:

當(dāng)然,這不是我們所期望的結(jié)果,在銷毀對象時,兩個對象的析構(gòu)函數(shù)將對同一個內(nèi)存空間釋放兩次,這就是錯誤出現(xiàn)的原因。我們需要的不是兩個p有相同的值,而是兩個p指向的空間有相同的值,解決辦法就是使用“深拷貝”。

2.3.3. 深拷貝

??? 在“深拷貝”的情況下,對于對象中動態(tài)成員,就不能僅僅簡單地賦值了,而應(yīng)該重新動態(tài)分配空間,如上面的例子就應(yīng)該按照如下的方式進行處理:

class Rect { public:Rect() // 構(gòu)造函數(shù),p指向堆中分配的一空間{p = new int(100);}Rect(const Rect& r){width = r.width;height = r.height;p = new int; // 為新對象重新動態(tài)分配空間*p = *(r.p);}~Rect() // 析構(gòu)函數(shù),釋放動態(tài)分配的空間{if(p != NULL){delete p;}} private:int width;int height;int *p; // 一指針成員 };

此時,在完成對象的復(fù)制后,內(nèi)存的一個大致情況如下:

此時rect1的p和rect2的p各自指向一段內(nèi)存空間,但它們指向的空間具有相同的內(nèi)容,這就是所謂的“深拷貝”。

2.3.4. 防止默認拷貝發(fā)生

??? 通過對對象復(fù)制的分析,我們發(fā)現(xiàn)對象的復(fù)制大多在進行“值傳遞”時發(fā)生,這里有一個小技巧可以防止按值傳遞——聲明一個私有拷貝構(gòu)造函數(shù)。甚至不必去定義這個拷貝構(gòu)造函數(shù),這樣因為拷貝構(gòu)造函數(shù)是私有的,如果用戶試圖按值傳遞或函數(shù)返回該類對象,將得到一個編譯錯誤,從而可以避免按值傳遞或返回對象。

// 防止按值傳遞 class CExample { private:int a;public://構(gòu)造函數(shù)CExample(int b){ a = b;cout<<"creat: "<<a<<endl;}private://拷貝構(gòu)造,只是聲明CExample(const CExample& C);public:~CExample(){cout<< "delete: "<<a<<endl;}void Show (){cout<<a<<endl;} };//全局函數(shù) void g_Fun(CExample C) {cout<<"test"<<endl; }int main() {CExample test(1);//g_Fun(test); 按值傳遞將出錯return 0; }

2.4. 拷貝構(gòu)造函數(shù)的幾個細節(jié)

1. 拷貝構(gòu)造函數(shù)里能調(diào)用private成員變量嗎?
解答:
這個問題是在網(wǎng)上見的,當(dāng)時一下子有點暈。其時從名子我們就知道拷貝構(gòu)造函數(shù)其時就是一個特殊的構(gòu)造函數(shù),操作的還是自己類的成員變量,所以不受private的限制。

2. 以下函數(shù)哪個是拷貝構(gòu)造函數(shù),為什么?

X::X(const X&); X::X(X); X::X(X&, int a=1); X::X(X&, int a=1, int b=2);

解答:對于一個類X, 如果一個構(gòu)造函數(shù)的第一個參數(shù)是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且沒有其他參數(shù)或其他參數(shù)都有默認值,那么這個函數(shù)是拷貝構(gòu)造函數(shù).

X::X(const X&); //是拷貝構(gòu)造函數(shù) X::X(X&, int=1); //是拷貝構(gòu)造函數(shù) X::X(X&, int a=1, int b=2); //當(dāng)然也是拷貝構(gòu)造函數(shù)

?3. 一個類中可以存在多于一個的拷貝構(gòu)造函數(shù)嗎?
解答:
類中可以存在超過一個拷貝構(gòu)造函數(shù)。

class X { public: X(const X&); // const 的拷貝構(gòu)造X(X&); // 非const的拷貝構(gòu)造 };

注意,如果一個類中只存在一個參數(shù)為 X& 的拷貝構(gòu)造函數(shù),那么就不能使用const X或volatile X的對象實行拷貝初始化.

class X { public:X(); X(X&); }; const X cx; X x = cx; // error

如果一個類中沒有定義拷貝構(gòu)造函數(shù),那么編譯器會自動產(chǎn)生一個默認的拷貝構(gòu)造函數(shù)。
這個默認的參數(shù)可能為?X::X(const X&)或?X::X(X&),由編譯器根據(jù)上下文決定選擇哪一個。

三、禁用拷貝構(gòu)造函數(shù)(源自為什么很多人禁用拷貝(復(fù)制)構(gòu)造函數(shù))

關(guān)于C++的拷貝構(gòu)造函數(shù),很多的建議是直接禁用。為什么大家會這么建議呢?沒有拷貝構(gòu) 造函數(shù)會有什么限制呢?如何禁用拷貝構(gòu)造呢?這篇文章對這些問題做一個簡單的總結(jié)。

這里討論的問題以拷貝構(gòu)造函數(shù)為例子,但是通常賦值操作符是通過拷貝構(gòu)造函數(shù)來實現(xiàn) 的( copy-and-swap 技術(shù),詳見《Exceptional C++》一書),所以這里討論也適用于賦 值操作符,通常來說禁用拷貝構(gòu)造函數(shù)的同時也會禁用賦值操作符。


3.1 為什么禁用拷貝構(gòu)造函數(shù)

關(guān)于拷貝構(gòu)造函數(shù)的禁用原因,我目前了解的主要是兩個原因。第一是淺拷貝問題,第二 個則是基類拷貝問題。

3.1.1 淺拷貝問題

編譯器默認生成的構(gòu)造函數(shù),是memberwise拷貝^1,也就是逐個拷貝成員變量,對于 下面這個類的定義

class Widget { public: Widget(const std::string &name) : name_(name), buf_(new char[10]) {} ~Widget() { delete buf_; } private: std::string name_; char *buf_; };

默認生成的拷貝構(gòu)造函數(shù),會直接拷貝buf_的值,導(dǎo)致兩個Widget對象指向同一個緩 沖區(qū),這會導(dǎo)致析構(gòu)的時候兩次刪除同一片區(qū)域的問題(這個問題又叫雙殺問題)。

解決這個問題的方式有很多:

  • 自己編寫拷貝構(gòu)造函數(shù),然后在拷貝構(gòu)造函數(shù)中創(chuàng)建新的buf_,不過拷貝構(gòu)造函數(shù)的 編寫需要考慮異常安全的問題,所以編寫起來有一定的難度。

  • 使用?shared_ptr?這樣的智能指針,讓所有的?Widget?對象共享一片?buf_,并 讓?shared_ptr?的引用計數(shù)機制幫你智能的處理刪除問題。

  • 禁用拷貝構(gòu)造函數(shù)和賦值操作符。如果你根本沒有打算讓W(xué)idget支持拷貝,你完全可 以直接禁用這兩操作,這樣一來,前面提到的這些問題就都不是問題了。

  • 3.1.2 基類拷貝構(gòu)造問題

    如果我們不去自己編寫拷貝構(gòu)造函數(shù),編譯器默認生成的版本會自動調(diào)用基類的拷貝構(gòu)造 函數(shù)完成基類的拷貝:

    class Base { public: Base() { cout << "Base Default Constructor" << endl; } Base(const Base &) { cout << "Base Copy Constructor" << endl; } }; class Drived : public Base { public: Drived() { cout << "Drived Default Constructor" << endl; } }; int main(void) { Drived d1; Drived d2(d1); }

    上面這段代碼的輸出如下:

    Base Default Constructor Drived Default Constructor Base Copy Constructor // 自動調(diào)用了基類的拷貝構(gòu)造函數(shù)

    但是如果我們出于某種原因編寫了,自己編寫了拷貝構(gòu)造函數(shù)(比如因為上文中提到的淺 拷貝問題),編譯器不會幫我們安插基類的拷貝構(gòu)造函數(shù),它只會在必要的時候幫我們安 插基類的默認構(gòu)造函數(shù):

    class Base { public: Base() { cout << "Base Default Constructor" << endl; } Base(const Base &) { cout << "Base Copy Constructor" << endl; } }; class Drived : public Base { public: Drived() { cout << "Drived Default Constructor" << endl; } Drived(const Drived& d) { cout << "Drived Copy Constructor" << endl; } }; int main(void) { Drived d1; Drived d2(d1); }

    上面這段代碼的輸出如下:

    Base Default Constructor Drived Default Constructor Base Default Constructor // 調(diào)用了基類的默認構(gòu)造函數(shù) Drived Copy Constructor

    這當(dāng)然不是我們想要看到的結(jié)果,為了能夠得到正確的結(jié)果,我們需要自己手動調(diào)用基類 的對應(yīng)版本拷貝基類對象。

    Drived(const Drived& d) : Base(d) { cout << "Drived Copy Constructor" << endl; }

    這本來不是什么問題,只不過有些人編寫拷貝構(gòu)造函數(shù)的時候會忘記這一點,所以導(dǎo)致基 類子對象沒有正常復(fù)制,造成很難察覺的BUG。所以為了一勞永逸的解決這些蛋疼的問題, 干脆就直接禁用拷貝構(gòu)造和賦值操作符。


    3.2 沒有拷貝構(gòu)造的限制

    在C++11之前對象必須有正常的拷貝語義才能放入容器中,禁用拷貝構(gòu)造的對象無法直接放 入容器中,當(dāng)然你可以使用指針來規(guī)避這一點,但是你又落入了自己管理指針的困境之中 (或許使用智能指針可以緩解這一問題)。

    C++11中存在移動語義,你可以通過移動而不是拷貝把數(shù)據(jù)放入容器中。

    拷貝構(gòu)造函數(shù)的另一個應(yīng)用在于設(shè)計模式中的原型模式,在C++中沒有拷貝構(gòu)造函數(shù),這 個模式實現(xiàn)可能比較困難。


    3.3 如何禁用拷貝構(gòu)造

  • 如果你的編譯器支持 C++11,直接使用?delete

  • 否則你可以把拷貝構(gòu)造函數(shù)和賦值操作符聲明成private同時不提供實現(xiàn)。

  • 你可以通過一個基類來封裝第二步,因為默認生成的拷貝構(gòu)造函數(shù)會自動調(diào)用基類的拷 貝構(gòu)造函數(shù),如果基類的拷貝構(gòu)造函數(shù)是?private,那么它無法訪問,也就無法正常 生成拷貝構(gòu)造函數(shù)。

  • class NonCopyable { protected: ~NonCopyable() {} // 關(guān)于為什么聲明成為 protected,參考 // 《Exceptional C++ Style》 private: NonCopyable(const NonCopyable&); } class Widget : private NonCopyable { // 關(guān)于為什么使用 private 繼承 // 參考《Effective C++》第三版 } Widget widget(Widget()); // 錯誤

    上不會生成memberwise的拷貝構(gòu)造函數(shù),詳細內(nèi)容可以參考《深度探索C++對象模型》一 書

    3.4??禁用拷貝


    禁用原因主要是兩個:
    1. 淺拷貝問題,也就是上面提到的二次析構(gòu)。
    2. 自定義了基類和派生類的拷貝構(gòu)造函數(shù),但派生類對象拷貝時,調(diào)用了派生類的拷貝,沒有調(diào)用自定義的基類拷貝而是調(diào)用默認的基類拷貝。這樣可能造成不安全,比如出現(xiàn)二次析構(gòu)問題時,因為不會調(diào)用我們自定義的基類深拷貝,還是默認的淺拷貝。

    Effective C++條款6規(guī)定,如果不想用編譯器自動生成的函數(shù),就應(yīng)該明確拒絕。方法一般有三種:
    1. C++11對函數(shù)聲明加delete關(guān)鍵字:Base(const Base& obj) = delete;,不必有函數(shù)體,這時再調(diào)用拷貝構(gòu)造會報錯嘗試引用已刪除的函數(shù)。
    2. 最簡單的方法是將拷貝構(gòu)造函數(shù)聲明為private
    3. 條款6給出了更好的處理方法:創(chuàng)建一個基類,聲明拷貝構(gòu)造函數(shù),但訪問權(quán)限是private,使用的類都繼承自這個基類。默認拷貝構(gòu)造函數(shù)會自動調(diào)用基類的拷貝構(gòu)造函數(shù),而基類的拷貝構(gòu)造函數(shù)是private,那么它無法訪問,也就無法正常生成拷貝構(gòu)造函數(shù)。

    Qt就是這樣做的,QObject定義中有這樣一段,三條都利用了:

    第一種方法:最簡單的方法是將拷貝構(gòu)造函數(shù)聲明為private

    private:Q_DISABLE_COPY(QMainWindow)#define Q_DISABLE_COPY(Class) \Class(const Class &) Q_DECL_EQ_DELETE;\Class &operator=(const Class &) Q_DECL_EQ_DELETE;


    類的不可拷貝特性是可以繼承的,例如凡是繼承自QObject的類都不能使用拷貝構(gòu)造函數(shù)和賦值運算符。
    ?

    (2)第二種方法 繼承一個uncopyable類
    C++的編譯在鏈接之前,如果我們能在編譯期解決這個問題,會節(jié)省不少的時間,要想在編譯期解決問題,就需要人為制造一些bug。我們聲明一個專門阻止拷貝的基類uncopyable。

    class uncopyable{ protected:uncopyable(){}~uncopyable(){} private:uncopyable(const uncopyable&);uncopyable& operator=(const uncopyable&); }


    接下來,我們的類只要繼承uncopyable,如果要發(fā)生拷貝,編譯器都會嘗試調(diào)用基類的拷貝構(gòu)造函數(shù)或者賦值運算符,但是因為這兩者是私有的,會出現(xiàn)編譯錯誤。

    四、C++類對象的賦值與復(fù)制(源自C++類對象的賦值與復(fù)制)

    本文主要介紹C++中類對象的賦值操作、復(fù)制操作,以及兩者之間的區(qū)別,另外還會講到“深拷貝”與“淺拷貝”的相關(guān)內(nèi)容。

    本系列內(nèi)容會分為三篇文章進行講解。

    4.1 對象的賦值


    4.1.1 what

    如同基本類型的賦值語句一樣,同一個類的對象之間也是可以進行賦值操作的,即將一個對象的值賦給另一個對象。

    對于類對象的賦值,只會對類中的數(shù)據(jù)成員進行賦值,而不對成員函數(shù)賦值。

    例如:obj1 和 obj2 是同一類 ClassA 的兩個對象,那么對象賦值語句“obj2 = obj1;” 就會把對象 obj1 的數(shù)據(jù)成員的值逐位賦給對象 obj2。


    4.1.2 代碼示例

    下面展示一個對象賦值的代碼示例(object_assign_and_copy_test1.cpp),如下:

    #include <iostream>using namespace std;class ClassA { public:// 設(shè)置成員變量的值void SetValue(int i, int j){m_nValue1 = i;m_nValue2 = j;}// 打印成員變量的值void ShowValue(){cout << "m_nValue1 is: " << m_nValue1 << ", m_nValue2 is: " << m_nValue2 << endl;} private:int m_nValue1;int m_nValue2; };int main() {// 聲明對象obj1和obj2ClassA obj1;ClassA obj2;obj1.SetValue(1, 2);// 對象賦值場景 —— 將obj1的值賦給obj2obj2 = obj1;cout << "obj1 info as followed: " << endl;obj1.ShowValue();cout << "obj2 info as followed: " << endl;obj2.ShowValue();return 0; }

    編譯并運行上述代碼,結(jié)果如下:

    上面的執(zhí)行結(jié)果表明,通過對象賦值語句,我們將obj1的值成功地賦給了obj2。

    4.1.3 幾點說明

    對于對象賦值,進行以下幾點說明:

    • 進行對象賦值時,兩個對象的必須屬于同一個類,如對象所述的類不同,在編譯時將會報錯;
    • 兩個對象之間的賦值,只會讓這兩個對象中數(shù)據(jù)成員相同,而兩個對象仍然是獨立的。例如在上面的示例代碼中,進行對象賦值后,再調(diào)用 obj1.set() 設(shè)置 obj1 的值,并不會影響到 obj2 的值;
    • 對象賦值是通過賦值運算函數(shù)實現(xiàn)的。每一個類都有默認的賦值運算符,我們也可以根據(jù)需要,對賦值運算符進行重載。一般來說,需要手動編寫析構(gòu)函數(shù)的類,都需要重載賦值運算符(具體原因下文會介紹);
    • 在對象聲明之后,進行的對象賦值運算,才屬于“真正的”對象賦值運算,即使用了賦值運算符“=”;而在對象初始化時,針對對象進行的賦值操作,其實是屬于對象的復(fù)制。示例如下:

    // 聲明obj1和obj2ClassA obj1;ClassA obj2;obj2 = obj1; // 此語句為對象的賦值// 聲明obj1ClassA obj1;// 聲明并初始化obj2ClassA obj2 = obj1; // 此語句屬于對象的復(fù)制

    4.1.4 進一步研究

    下面從內(nèi)存分配的角度分析一下對象的賦值操作。


    4.1.4.1 C++中對象的內(nèi)存分配方式

    在C++中,只要聲明了對象,對象實例在編譯的時候,系統(tǒng)就需要為其分配內(nèi)存了。一段代碼示例如下:

    class ClassA { public:ClassA(int id, char* name){m_nId = id;m_pszName = new char[strlen(name) + 1];strcpy(m_pszName, name);} private:char* m_pszName;int m_nId; };int main() {ClassA obj1(1, "liitdar");ClassA obj2;return 0; }

    在上述代碼編譯之后,系統(tǒng)為 obj1 和 obj2 都分配相應(yīng)大小的內(nèi)存空間(只不過對象 obj1 的內(nèi)存域被初始化了,而 obj2 的內(nèi)存域的值為隨機值)。兩者的內(nèi)存分配效果如下:


    4.1.4.2?默認的賦值運算符

    延續(xù)上面的示例代碼,我們執(zhí)行“obj2 = obj1;”,即利用默認的賦值運算符將對象 obj1 的值賦給 obj2。使用類中默認的賦值運算符,會將對象中的所有位于 stack 中的域進行相應(yīng)的復(fù)制操作;同時,如果對象有位于 heap 上的域,則不會為目標對象分配 heap 上的空間,而只是讓目標對象指向源對象 heap 上的同一個地址。

    執(zhí)行了“obj2 = obj1;”默認的賦值運算后,兩個對象的內(nèi)存分配效果如下:

    因此,對于類中默認的賦值運算,如果源對象域內(nèi)沒有 heap 上的空間,其不會產(chǎn)生任何問題。但是,如果源對象域內(nèi)需要申請 heap 上的空間,那么由于源對象和目標對象都指向?heap 的同一段內(nèi)容,所以在析構(gòu)對象的時候,就會連續(xù)兩次釋放 heap 上的那一塊內(nèi)存區(qū)域,從而導(dǎo)致程序異常。

    ~ClassA(){delete m_pszName;}

    1.4.3?解決方案

    為了解決上面的問題,如果對象會在 heap 上存在內(nèi)存域,則我們必須重載賦值運算符,從而在進行對象的賦值操作時,使不同對象的成員域指向不同的 heap 地址。

    重載賦值運算符的代碼如下:

    // 賦值運算符重載需要返回對象的引用,否則返回后其值立即消失ClassA& operator=(ClassA& obj){// 釋放heap內(nèi)存if (m_pszName != NULL){delete m_pszName;}// 賦值stack內(nèi)存的值this->m_nId = obj.m_nId;// 賦值heap內(nèi)存的值int nLength = strlen(obj.m_pszName);m_pszName = new char[nLength + 1];strcpy(m_pszName, obj.m_pszName);return *this;}

    使用上面重載后的賦值運算符對對象進行賦值時,兩個對象的內(nèi)存分配效果如下:

    這樣,在對象 obj1、obj2 退出其的作用域,調(diào)用相應(yīng)的析構(gòu)函數(shù)時,就會釋放不同 heap 空間的內(nèi)存,也就不會出現(xiàn)程序異常了。

    4.2 對象的復(fù)制(源自C++類對象的賦值與復(fù)制(二))

    4.2.1 what

    相對于“對已聲明的對象使用賦值運算符進行的對象賦值”操作,使用拷貝構(gòu)造函數(shù)操作對象的方式,稱為“對象的復(fù)制”。

    類的拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),其形參是本類對象的引用。拷貝構(gòu)造函數(shù)的作用為:在創(chuàng)建一個新對象時,使用一個已經(jīng)存在的對象去初始化這個新對象。例如語句“ClassA?obj2(obj1);”就使用了拷貝構(gòu)造函數(shù),該語句在創(chuàng)建新對象 obj2 時,利用已經(jīng)存在的對象 obj1 去初始化對象 obj2。

    對象的賦值與對象的復(fù)制,貌似都是只對類的成員變量進行拷貝,而不會對類的成員函數(shù)進行操作。—— 待進一步確認。

    4.2.2 拷貝構(gòu)造函數(shù)的特點

    拷貝構(gòu)造函數(shù)有以下特點:

    • 拷貝構(gòu)造函數(shù)也是一種構(gòu)造函數(shù),所以其函數(shù)名與類名相同,并且該函數(shù)也沒有返回值類型;
    • 拷貝構(gòu)造函數(shù)只有一個參數(shù),并且該參數(shù)是其所屬類對象的引用;
    • 每一個類都必須有一個拷貝構(gòu)造函數(shù),我們可以根據(jù)需要重載默認的拷貝構(gòu)造函數(shù)(自定義拷貝構(gòu)造函數(shù)),如果沒有重載默認的拷貝構(gòu)造函數(shù),系統(tǒng)就會生成產(chǎn)生一個默認的拷貝構(gòu)造函數(shù),默認的拷貝構(gòu)造函數(shù)將會復(fù)制出一個數(shù)據(jù)成員完全相同的新對象;

    4.2.3 自定義拷貝構(gòu)造函數(shù)

    這里展示一個自定義拷貝構(gòu)造函數(shù)的代碼示例(object_assign_and_copy_test2.cpp),如下:

    #include <iostream>using namespace std;class ClassA { public:// 普通構(gòu)造函數(shù)ClassA(int i, int j){m_nValue1 = i;m_nValue2 = j;}// 自定義的拷貝構(gòu)造函數(shù)ClassA(const ClassA& obj){m_nValue1 = obj.m_nValue1 * 2;m_nValue2 = obj.m_nValue2 * 2;}// 打印成員變量的值void ShowValue(){cout << "m_nValue1 is: " << m_nValue1 << ", m_nValue2 is: " << m_nValue2 << endl;} private:int m_nValue1;int m_nValue2; };int main() {// 創(chuàng)建并初始化對象obj1,此處調(diào)用了普通構(gòu)造函數(shù)ClassA obj1(1, 2);// 創(chuàng)建并初始化對象obj2,此處調(diào)用了自定義的拷貝構(gòu)造函數(shù)ClassA obj2(obj1);obj1.ShowValue();obj2.ShowValue();return 0; }

    編譯并執(zhí)行上述代碼,結(jié)果如下:

    上述執(zhí)行結(jié)果表明,通過調(diào)用自定義的拷貝構(gòu)造函數(shù),我們在創(chuàng)建對象 obj2 時,結(jié)合對象 obj1 的成員變量的值,完成了我們自定義的初始化過程。

    4.2.4 調(diào)用形式上的區(qū)別

    我們可以從調(diào)用形式上,對“對象的賦值”和“對象的復(fù)制”進行區(qū)分。在此,我們列出一些對應(yīng)關(guān)系:

    • 對象的賦值:指的是調(diào)用了類的賦值運算符,進行的對象的拷貝操作;
    • 對象的復(fù)制:指的是調(diào)用了類的拷貝構(gòu)造函數(shù),進行的對象的拷貝操作。

    上面的對應(yīng)關(guān)系是不嚴謹?shù)?#xff0c;因為有些情況下,即使使用了賦值運算符“=”,但其實最終使用的仍然是類的拷貝構(gòu)造函數(shù),這就引出了拷貝構(gòu)造函數(shù)的兩種調(diào)用形式。

    拷貝構(gòu)造函數(shù)的調(diào)用語法分為兩種:

    • 類名 對象2(對象1)。例如:“ClassA obj2(obj1);”,這種調(diào)用拷貝構(gòu)造函數(shù)的方法稱為“代入法”;
    • 類名 對象2 = 對象1。例如:“ClassA obj2 = obj1;”,這種調(diào)用拷貝構(gòu)造函數(shù)的方法稱為“賦值法”。

    拷貝構(gòu)造函數(shù)的“賦值法”就很容易與“對象的賦值”場景混淆,其二者之間的區(qū)別是:對象的賦值場景必須是建立在源對象與目標對象均已聲明的基礎(chǔ)上;而拷貝構(gòu)造函數(shù)函數(shù)的賦值法,必須是針對新創(chuàng)建對象的場景。代碼如下:

    【對象的賦值】:

    // 聲明對象obj1和obj2ClassA obj1;ClassA obj2;obj1.SetValue(1, 2);// 對象賦值場景 —— 將obj1的值賦給obj2obj2 = obj1;

    【拷貝構(gòu)造函數(shù)的“賦值法”】:

    // 創(chuàng)建并初始化對象obj1,此處調(diào)用了普通構(gòu)造函數(shù)ClassA obj1(1, 2);// 創(chuàng)建并初始化對象obj2,此處調(diào)用了自定義的拷貝構(gòu)造函數(shù)ClassA obj2 = obj1;

    當(dāng)然,為了代碼的清晰化,建議使用拷貝構(gòu)造函數(shù)的“代入法”,更可以讓人一眼就看出調(diào)用的是拷貝構(gòu)造函數(shù)。

    4.2.5 調(diào)用拷貝構(gòu)造函數(shù)的三個場景

    4.2.5.1 類對象初始化

    當(dāng)使用類的一個對象去初始化另一個對象時,會調(diào)用拷貝構(gòu)造函數(shù)(包括“代入法”和“賦值法”)。示例代碼如下:

    // 創(chuàng)建并初始化對象obj1,此處調(diào)用了普通構(gòu)造函數(shù)ClassA obj1(1, 2);// 創(chuàng)建并初始化對象obj2,此處調(diào)用了自定義的拷貝構(gòu)造函數(shù)ClassA obj2 = obj1; // 代入法ClassA obj3 = obj1; // 賦值法

    4.2.5.2?類對象作為函數(shù)參數(shù)

    當(dāng)類對象作為函數(shù)形參時,在調(diào)用函數(shù)進行形參和實參轉(zhuǎn)換時,會調(diào)用拷貝構(gòu)造函數(shù)。示例代碼如下:

    // 形參是類ClassA的對象obj void funA(ClassA obj) {obj.ShowValue(); }int main() {ClassA obj1(1, 2);// 調(diào)用函數(shù)funA時,實參obj1是類ClassA的對象// 這里會調(diào)用拷貝構(gòu)造函數(shù),使用實參obj1初始化形參對象objfunA(obj1);return 0; }

    說明:在上面的main函數(shù)內(nèi),語句“funA(obj1);”就會調(diào)用拷貝構(gòu)造函數(shù)。


    4.2.5.3?類對象作為函數(shù)返回值

    當(dāng)函數(shù)的返回值是類的對象、在函數(shù)調(diào)用完畢將返回值(對象)帶回函數(shù)調(diào)用處,此時會調(diào)用拷貝構(gòu)造函數(shù),將函數(shù)返回的對象賦值給一個臨時對象,并傳到函數(shù)的調(diào)用處。示例代碼如下:

    // 函數(shù)funB()的返回值類型是ClassA類類型 ClassA funB() {ClassA obj1(1, 2);// 函數(shù)的返回值是ClassA類的對象return obj1; }int main() {// 定義類ClassA的對象obj2ClassA obj2;// funB()函數(shù)執(zhí)行完成、返回調(diào)用處時,會調(diào)用拷貝構(gòu)造函數(shù)// 使用obj1初始化obj2obj2 = funB();return 0; }

    說明:在上面的main函數(shù)內(nèi),語句“obj2 = funB();”就會調(diào)用拷貝構(gòu)造函數(shù)。由于對象obj1是函數(shù)funB中定義的,在函數(shù)funB結(jié)束時,obj1的生命周期就結(jié)束了,因此在函數(shù)funB結(jié)束之前,執(zhí)行語句"return obj1"時,會調(diào)用拷貝構(gòu)造函數(shù)將obj1的值拷貝到一個
    臨時對象中,這個臨時對象是系統(tǒng)在主程序中臨時創(chuàng)建的。funB函數(shù)結(jié)束時,對象obj1消失,但是臨時對象將會通過語句“obj2 = funB()”賦值給對象obj2,執(zhí)行完這條語句后,臨時對象也自動消失了。?

    4.3 淺拷貝(源自C++類對象的賦值與復(fù)制(三))

    4.3.1 what

    淺拷貝:就是只拷貝類中位于 stack 域中的內(nèi)容,而不會拷貝 heap 域中的內(nèi)容。

    例如,使用類的默認的賦值運算符“=”,或默認的拷貝構(gòu)造函數(shù)時,進行的對象拷貝都屬于淺拷貝。這也說明,“淺拷貝”與使用哪種方式(賦值運算符或是拷貝構(gòu)造函數(shù))進行對象拷貝無關(guān)。

    4.3.2 問題

    淺拷貝會有一個問題,當(dāng)類中存在指針成員變量時,進行淺拷貝后,目標對象與源對象的該指針成員變量將會指向同一塊 heap 內(nèi)存(而非每個對象單獨一塊內(nèi)存),這就會導(dǎo)致由于共用該段內(nèi)存而產(chǎn)生的內(nèi)存覆蓋、重復(fù)釋放內(nèi)存等等問題。詳情可參考本系列第一章內(nèi)容。

    所以,對于帶有指針的類對象的拷貝操作,正確的做法應(yīng)當(dāng)使兩個對象的指針指向各自不同的內(nèi)存,即在拷貝時不是簡單地拷貝指針,而是將指針指向的內(nèi)存中的每一個元素都進行拷貝。由此也就引出了“深拷貝”的概念。

    4.4?深拷貝

    深拷貝:當(dāng)進行對象拷貝時,將對象位于?stack 域和 heap 域中的數(shù)據(jù)都進行拷貝。

    前面也提到了,類默認提供的賦值運算符或拷貝構(gòu)造函數(shù),進行的都是淺拷貝,所以,為了實現(xiàn)對象的深拷貝,我們需要對賦值運算符或拷貝構(gòu)造函數(shù)進行重載,以達到深拷貝的目的。

    4.4.1 賦值運算符的重載

    這里展示一段重載賦值運算符的示例代碼,如下:

    // 重載賦值運算符ClassA& operaton= (ClassA& obj){// 拷貝 stack 域的值m_nId = obj.m_nId;// 適應(yīng)自賦值(obj = obj)操作if (this == &a){return *this;}// 釋放掉已有的 heap 空間if (m_pszName != NULL){delete m_pszName;}// 新建 heap 空間m_pszName = new char[strlen(obj.m_pszName) + 1];// 拷貝 heap 空間的內(nèi)容if (m_pszName != NULL){strcpy(m_pszName, obj.m_pszName);}return *this;}private:int m_nId;char* m_pszName;

    4.4.2?拷貝構(gòu)造函數(shù)的重載

    這里展示一段重載拷貝構(gòu)造函數(shù)的示例代碼,如下:

    // 重載拷貝構(gòu)造函數(shù),重載后的拷貝構(gòu)造函數(shù)支持深拷貝ClassA(ClassA &obj){// 拷貝 stack 域的值m_nId = obj.m_nId;// 新建 heap 空間m_pszName = new char[strlen(obj.m_pszName) + 1];// 拷貝 heap 空間的內(nèi)容if (m_pszName != NULL){strcpy(m_pszName, obj.m_pszName);}}private:int m_nId;char* m_pszName;

    4.4.3?總結(jié)

    從上述兩個示例代碼可以看出,支持深拷貝的重載賦值運算符和重載拷貝構(gòu)造函數(shù)相似,但兩者也存在以下區(qū)別:

    • 重載賦值運算符最好有返回值,以方便進行鏈式賦值(obj3=obj2=obj1),返回值類型也最好是對象的引用;而重載拷貝構(gòu)造函數(shù)因為屬于構(gòu)造函數(shù)的一種,所以不需要返回值;
    • 重載賦值運算符首先要釋放掉對象自身的 heap 空間(如果存在的話),然后再進行 heap 內(nèi)容的拷貝操作;而重載拷貝構(gòu)造函數(shù)無需如此,因為拷貝構(gòu)造函數(shù)函數(shù)是在創(chuàng)建(并初始化)對象時調(diào)用的,對象此時還沒有分配 heap 空間呢。
    • 如果在重載賦值運算符和重載拷貝構(gòu)造函數(shù)都可以解決問題時,建議選擇重載拷貝構(gòu)造函數(shù),因為貌似坑少一些^-^。

    五、C++中構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別和實現(xiàn)(源自C++中構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別和實現(xiàn))

    C++中一般創(chuàng)建對象,拷貝或賦值的方式有構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)這三種方法。下面就詳細比較下三者之間的區(qū)別以及它們的具體實現(xiàn)

    5.1.構(gòu)造函數(shù)

    構(gòu)造函數(shù)是一種特殊的類成員函數(shù),是當(dāng)創(chuàng)建一個類的對象時,它被調(diào)用來對類的數(shù)據(jù)成員進行初始化和分配內(nèi)存。(構(gòu)造函數(shù)的命名必須和類名完全相同)

    首先說一下一個C++的空類,編譯器會加入哪些默認的成員函數(shù)

    ·默認構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)

    ·析構(gòu)函數(shù)

    ·賦值函數(shù)(賦值運算符)

    ·取值函數(shù)

    **即使程序沒定義任何成員,編譯器也會插入以上的函數(shù)!

    注意:構(gòu)造函數(shù)可以被重載,可以多個,可以帶參數(shù);

    析構(gòu)函數(shù)只有一個,不能被重載,不帶參數(shù)

    而默認構(gòu)造函數(shù)沒有參數(shù),它什么也不做。當(dāng)沒有重載無參構(gòu)造函數(shù)時,

    ? A a就是通過默認構(gòu)造函數(shù)來創(chuàng)建一個對象

    下面代碼為構(gòu)造函數(shù)重載的實現(xiàn)

    <span style="font-size:14px;">class A { int m_i; Public:A() {Cout<<”無參構(gòu)造函數(shù)”<<endl; } A(int i):m_i(i) {} //初始化列表 }</span>

    5.2.拷貝構(gòu)造函數(shù)

    拷貝構(gòu)造函數(shù)是C++獨有的,它是一種特殊的構(gòu)造函數(shù),用基于同一類的一個對象構(gòu)造和初始化另一個對象。

    當(dāng)沒有重載拷貝構(gòu)造函數(shù)時,通過默認拷貝構(gòu)造函數(shù)來創(chuàng)建一個對象

    A a;

    A b(a);

    A b=a; ?都是拷貝構(gòu)造函數(shù)來創(chuàng)建對象b

    強調(diào):這里b對象是不存在的,是用a 對象來構(gòu)造和初始化b的!!

    先說下什么時候拷貝構(gòu)造函數(shù)會被調(diào)用:

    在C++中,3種對象需要復(fù)制,此時拷貝構(gòu)造函數(shù)會被調(diào)用

    1)一個對象以值傳遞的方式傳入函數(shù)體

    2)一個對象以值傳遞的方式從函數(shù)返回

    3)一個對象需要通過另一個對象進行初始化

    什么時候編譯器會生成默認的拷貝構(gòu)造函數(shù):

    1)如果用戶沒有自定義拷貝構(gòu)造函數(shù),并且在代碼中使用到了拷貝構(gòu)造函數(shù),編譯器就會生成默認的拷貝構(gòu)造函數(shù)。但如果用戶定義了拷貝構(gòu)造函數(shù),編譯器就不在生成。

    2)如果用戶定義了一個構(gòu)造函數(shù),但不是拷貝構(gòu)造函數(shù),而此時代碼中又用到了拷貝構(gòu)造函數(shù),那編譯器也會生成默認的拷貝構(gòu)造函數(shù)。

    因為系統(tǒng)提供的默認拷貝構(gòu)造函數(shù)工作方式是內(nèi)存拷貝,也就是淺拷貝。如果對象中用到了需要手動釋放的對象,則會出現(xiàn)問題,這時就要手動重載拷貝構(gòu)造函數(shù),實現(xiàn)深拷貝。

    下面說說深拷貝與淺拷貝:

    淺拷貝:如果復(fù)制的對象中引用了一個外部內(nèi)容(例如分配在堆上的數(shù)據(jù)),那么在復(fù)制這個對象的時候,讓新舊兩個對象指向同一個外部內(nèi)容,就是淺拷貝。(指針雖然復(fù)制了,但所指向的空間內(nèi)容并沒有復(fù)制,而是由兩個對象共用,兩個對象不獨立,刪除空間存在)

    深拷貝:如果在復(fù)制這個對象的時候為新對象制作了外部對象的獨立復(fù)制,就是深拷貝。

    拷貝構(gòu)造函數(shù)重載聲明如下:

    A (const A&other)

    下面為拷貝構(gòu)造函數(shù)的實現(xiàn):

    <span style="font-size:14px;">class A {int m_iA(const A& other):m_i(other.m_i) {Cout<<”拷貝構(gòu)造函數(shù)”<<endl; } }</span>

    5.3.賦值函數(shù)

    當(dāng)一個類的對象向該類的另一個對象賦值時,就會用到該類的賦值函數(shù)。

    當(dāng)沒有重載賦值函數(shù)(賦值運算符)時,通過默認賦值函數(shù)來進行賦值操作

    A a;

    A b;

    b=a;?

    強調(diào):這里a,b對象是已經(jīng)存在的,是用a 對象來賦值給b的!!

    賦值運算的重載聲明如下:

    ?A& operator = (const A& other)

    通常大家會對拷貝構(gòu)造函數(shù)和賦值函數(shù)混淆,這兒仔細比較兩者的區(qū)別:

    1)拷貝構(gòu)造函數(shù)是一個對象初始化一塊內(nèi)存區(qū)域,這塊內(nèi)存就是新對象的內(nèi)存區(qū),而賦值函數(shù)是對于一個已經(jīng)被初始化的對象來進行賦值操作。

    class A; A a; A b=a; //調(diào)用拷貝構(gòu)造函數(shù)(b不存在) A c(a) ; //調(diào)用拷貝構(gòu)造函數(shù)/****/class A; A a; A b; b = a ; //調(diào)用賦值函數(shù)(b存在)</span>

    2)一般來說在數(shù)據(jù)成員包含指針對象的時候,需要考慮兩種不同的處理需求:一種是復(fù)制指針對象,另一種是引用指針對象。拷貝構(gòu)造函數(shù)大多數(shù)情況下是復(fù)制,而賦值函數(shù)是引用對象

    3)實現(xiàn)不一樣。拷貝構(gòu)造函數(shù)首先是一個構(gòu)造函數(shù),它調(diào)用時候是通過參數(shù)的對象初始化產(chǎn)生一個對象。賦值函數(shù)則是把一個新的對象賦值給一個原有的對象,所以如果原來的對象中有內(nèi)存分配要先把內(nèi)存釋放掉,而且還要檢察一下兩個對象是不是同一個對象,如果是,不做任何操作,直接返回。(這些要點會在下面的String實現(xiàn)代碼中體現(xiàn))

    ?

    !!!如果不想寫拷貝構(gòu)造函數(shù)和賦值函數(shù),又不允許別人使用編譯器生成的缺省函數(shù),最簡單的辦法是將拷貝構(gòu)造函數(shù)和賦值函數(shù)聲明為私有函數(shù),不用編寫代碼。如:

    <span style="font-size:14px;">class A {private:A(const A& a); //私有拷貝構(gòu)造函數(shù)A& operate=(const A& a); //私有賦值函數(shù) }</span>

    如果程序這樣寫就會出錯:

    <span style="font-size:14px;">A a; A b(a); //調(diào)用了私有拷貝構(gòu)造函數(shù),編譯出錯A b; b=a; //調(diào)用了私有賦值函數(shù),編譯出錯</span>

    所以如果類定義中有指針或引用變量或?qū)ο?#xff0c;為了避免潛在錯誤,最好重載拷貝構(gòu)造函數(shù)和賦值函數(shù)。

    下面以string類的實現(xiàn)為例,完整的寫了普通構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)的實現(xiàn)。String類的基本實現(xiàn)見我另一篇博文。

    String::String(const char* str) //普通構(gòu)造函數(shù){cout<<construct<<endl;if(str==NULL) //如果str 為NULL,就存一個空字符串“”{m_string=new char[1];*m_string ='\0'; }else{m_string= new char[strlen(str)+1] ; //分配空間strcpy(m_string,str);}}String::String(const String&other) //拷貝構(gòu)造函數(shù){cout<<"copy construct"<<endl;m_string=new char[strlen(other.m_string)+1]; //分配空間并拷貝strcpy(m_string,other.m_string); }String & String::operator=(const String& other) //賦值運算符 {cout<<"operator =funtion"<<endl ;if(this==&other) //如果對象和other是用一個對象,直接返回本身{return *this;}delete []m_string; //先釋放原來的內(nèi)存m_string= new char[strlen(other.m_string)+1];strcpy(m_string,other.m_string);return * this; }</span>

    一句話記住三者:對象不存在,且沒用別的對象來初始化,就是調(diào)用了構(gòu)造函數(shù);

    ??????????????? 對象不存在,且用別的對象來初始化,就是拷貝構(gòu)造函數(shù)(上面說了三種用它的情況!)

    ???????????????? 對象存在,用別的對象來給它賦值,就是賦值函數(shù)。

    以上為本人結(jié)合很多資料和圖書整理出來的,將核心的點都系統(tǒng)的理出來,全自己按條理寫的,現(xiàn)在大家對普通構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)的區(qū)別和實現(xiàn)應(yīng)該都清楚了。

    六、C++類禁止copy構(gòu)造函數(shù)和copy assign操作符(C++類禁止copy構(gòu)造函數(shù)和copy assign操作符 - 十|里 - 博客園)

    在C++類中,編譯器可以暗自為class創(chuàng)建default構(gòu)造函數(shù)、copy構(gòu)造函數(shù)、copy assignment操作符,以及析構(gòu)函數(shù)。注意,這些編譯器產(chǎn)生出來的函數(shù)都是public的,為了阻止這些函數(shù)被創(chuàng)建出來,我們可以把它們聲明為private,這樣就阻止了編譯器暗自創(chuàng)建其對應(yīng)版本函數(shù)。

    class Node { public: Node(int _data = 0) : data(_data) {} int get() const { return data; } void set(int _data) { data = _data; } private: Node(const Node &); Node &operator=(const Node &); int data; };

    在上面的class定義中,當(dāng)程序企圖拷貝Node對象時,編譯器就會阻止該操作。這樣的話,只要將copy構(gòu)造函數(shù)和copy assign操作符聲明為private就可以了,還有另外一種方式,我們可以專門定義一個阻止copying動作的base class。這個base class如下所示:

    class Uncopyable { protected: Uncopyable() {} // 允許derived對象構(gòu)造和析構(gòu) ~Uncopyable() {} private: Uncopyable(const Uncopyable &); // 阻止copying Uncopyable &operator=(const Uncopyable &); }; class Node : private Uncopyable { public: Node(int _data = 0) : data(_data) {} int get() const { return data; } void set(int _data) { data = _data; } private: int data; };

    這樣的話,在程序中,甚至在member函數(shù)或friend函數(shù)中,嘗試拷貝Node對象,編譯器就會試著生成一個copy構(gòu)造函數(shù)或copy assign操作符,這些函數(shù)的“默認版本”會嘗試調(diào)用其base class的對應(yīng)函數(shù),但是這些調(diào)用會被阻止,因為它們是private的,即阻止了該類對象的copy操作。

    ?

    ?

    ?

    與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的C++构造函数总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    国产亚洲精品bv在线观看 | 丁香婷婷色月天 | 99精品久久久久 | 成人91av | 国产成人一区二区三区在线观看 | 天天性天天草 | 国产黄色一级大片 | a级国产乱理论片在线观看 伊人宗合网 | 一级黄色片在线免费观看 | av免费网站 | 久久久免费视频播放 | 在线观看黄色免费视频 | 日日夜夜人人天天 | 九九热av | 91精品欧美一区二区三区 | 色婷婷在线视频 | 日日干夜夜操视频 | 在线国产精品视频 | 亚洲人片在线观看 | 人人草人人草 | 中文字幕av在线免费 | 中国一级特黄毛片大片久久 | 91高清免费在线观看 | 最新色站 | 精品视频不卡 | 91毛片在线 | 天天艹天天| 久久乐九色婷婷综合色狠狠182 | 久久久资源网 | 精品亚洲欧美一区 | 日韩精品一区二区三区免费观看 | 欧美日本不卡 | 欧美经典久久 | 成人动漫一区二区三区 | 久热国产视频 | 伊人狠狠色丁香婷婷综合 | 99色在线播放 | 国产精品久久久久久久久久白浆 | 久久婷婷视频 | 成人av一区二区在线观看 | 米奇影视7777| 国产高清不卡 | 亚洲视频精品在线 | 国产在线一区观看 | 免费福利小视频 | 天天操狠狠操夜夜操 | 免费在线观看日韩欧美 | 精品少妇一区二区三区在线 | 日韩精品一区二区三区高清免费 | 日日添夜夜添 | 亚洲成人精品久久 | 激情影院在线 | 久久激情小视频 | 日韩av网站在线播放 | 国产亚洲精品久久久久秋 | 在线观看免费视频你懂的 | 国产一二区视频 | 免费观看性生交 | 亚洲精品乱码久久久久久蜜桃动漫 | 国产精品麻豆91 | 中国一级片在线播放 | 精品国产伦一区二区三区免费 | 亚洲va欧美 | 午夜视频一区二区三区 | 国产日韩亚洲 | 五月天激情电影 | 成人免费观看视频大全 | 91探花国产综合在线精品 | 欧美日韩视频在线观看免费 | 国产区久久 | 黄色软件网站在线观看 | 五月婷婷操 | 国产中文自拍 | av一级片在线观看 | 日批视频在线观看免费 | 精品视频9999 | 毛片3| 天堂网一区 | 免费观看国产成人 | 国产v亚洲v | 国产精品嫩草影院99网站 | 国产一级视频在线免费观看 | 伊人狠狠色丁香婷婷综合 | 韩日精品在线 | 欧美精品一区二区在线播放 | 亚洲一二区视频 | 精品久久一区二区 | 国产精品丝袜在线 | 天天射天天干天天爽 | 在线观看av的网站 | 国产午夜精品免费一区二区三区视频 | 中文字幕2021 | 一区电影| 国产午夜精品一区二区三区在线观看 | 久久综合国产伦精品免费 | 一区二区三区免费网站 | 欧美日韩免费视频 | 国内三级在线观看 | 久久一区二区免费视频 | 国产直播av | www.成人精品 | h视频日本| 美女在线免费观看视频 | 国产在线 一区二区三区 | 久久久婷| 一本色道久久精品 | 91在线免费播放视频 | 久久网页 | 欧美三人交 | 精品国产一区二区三区av性色 | 密桃av在线 | 免费视频在线观看网站 | 成人在线观看资源 | 92精品国产成人观看免费 | 人人爽人人爽人人片 | www视频免费在线观看 | 在线观看91网站 | 国产亚洲视频在线 | 久久美女免费视频 | av电影av在线| 久久99久久99精品免费看小说 | 人人草在线视频 | 婷婷网五月天 | 亚洲一区二区三区miaa149 | 精品久久久久久久久久 | 日韩成人在线一区二区 | 99亚洲国产 | 国产色视频一区二区三区qq号 | 久久免费激情视频 | www.福利| 色网av| 亚洲一区二区高潮无套美女 | 精品国产一区二区久久 | 最近中文字幕免费 | 国产一级高清 | 午夜精品福利一区二区三区蜜桃 | 久久综合色播五月 | 激情欧美日韩一区二区 | 欧美激情一区不卡 | 欧美视频99 | 亚洲精品在线看 | 国产97免费 | 国产91精品一区二区麻豆亚洲 | 日日干影院 | 黄色一级在线视频 | 久久经典国产 | 欧美日韩性生活 | 国产精品a久久久久 | 国产精品美女久久久免费 | 亚洲丝袜一区二区 | 在线观看91| 午夜视频在线观看一区二区 | 国产精品美女久久久久久久 | 午夜视频导航 | 欧美日韩视频观看 | 一区二区三区免费在线播放 | 黄影院| 手机在线观看国产精品 | 91大神视频网站 | 日韩女同av | 中文字幕av电影下载 | 综合久久综合久久 | 国产高清免费 | 国产视频中文字幕 | 成人午夜电影免费在线观看 | 最近免费中文字幕大全高清10 | 成人毛片网 | 日韩精品免费一区二区在线观看 | 丁香婷婷激情 | 国产高清不卡在线 | 国产精品久久久久久久久久久久冷 | 婷婷丁香狠狠爱 | 制服丝袜亚洲 | 国产一级黄大片 | 国产在线色视频 | 国产精彩视频 | 成人精品视频久久久久 | 国产精品免费视频一区二区 | 香蕉蜜桃视频 | 中文字幕三区 | www激情com| 免费91在线观看 | 日韩高清三区 | 国产在线免费观看 | 久久久久99精品成人片三人毛片 | 亚洲一级影院 | 日韩免费在线一区 | 99热播精品| 久久艹国产 | 亚洲精品av中文字幕在线在线 | 在线不卡中文字幕播放 | 日韩 在线观看 | 中文字幕观看视频 | 中文字幕二区在线观看 | 夜夜视频资源 | 日本女人b | 毛片网在线 | 亚洲va在线va天堂 | 久久99电影 | 中文字幕中文字幕中文字幕 | 狠狠操天天射 | 黄色片视频在线观看 | 精品亚洲欧美一区 | 91福利区一区二区三区 | 欧美电影在线观看 | 国产精品观看在线亚洲人成网 | 免费高清男女打扑克视频 | 日韩av网站在线播放 | 夜夜骑首页| 缴情综合网五月天 | 黄色国产精品 | 青春草免费在线视频 | 98久9在线 | 免费 | 久久深爱网| 91精品国自产拍天天拍 | 国产日韩在线观看一区 | 在线看片视频 | 51久久夜色精品国产麻豆 | 国产r级在线观看 | 探花国产在线 | 久久精品老司机 | 亚洲激情在线 | a极黄色片 | 97麻豆视频 | 欧美日本一二三 | 黄色中文字幕 | 狠狠操狠狠干天天操 | 特级西西444www高清大视频 | 在线亚洲激情 | ww亚洲ww亚在线观看 | 成人免费ⅴa | 最新日韩在线观看视频 | av电影免费在线看 | 91看片淫黄大片一级在线观看 | 欧美日韩中文国产一区发布 | 欧亚日韩精品一区二区在线 | 日本久久免费视频 | 91丨九色丨国产在线 | 在线观看涩涩 | 成人小视频在线播放 | 国产尤物在线观看 | 在线v片| 国产又粗又猛又黄又爽的视频 | 日韩视频一 | 中文字幕 国产视频 | 国产精品久久久久久久久婷婷 | 国产精品精品国产婷婷这里av | 亚洲午夜精品福利 | 在线观看中文字幕dvd播放 | 麻豆视频免费看 | 成人av在线一区二区 | 国产色婷婷精品综合在线手机播放 | 91成年人网站 | 久久99精品久久久久久久久久久久 | 97视频免费观看2区 亚洲视屏 | 国产91综合一区在线观看 | 天堂va在线高清一区 | 成年人免费电影 | 日韩中字在线观看 | 中文字幕免费国产精品 | 最近中文字幕 | 在线成人看片 | 国内毛片毛片 | 日韩精品中文字幕一区二区 | 国内偷拍精品视频 | 波多野结衣一区 | 国际精品久久久 | 国内精品久久久久久久久久清纯 | 成人啊 v| 久久久久久久久久久久久久电影 | 久久ww| 特黄色大片 | 国产在线观看污片 | 亚洲aⅴ乱码精品成人区 | 国产精品中文字幕在线观看 | 亚洲精品久久久久中文字幕m男 | 久久黄色美女 | 国产精品不卡av | 久久综合婷婷国产二区高清 | 免费一级片在线观看 | 精品国产一区二区三区久久久久久 | 丁香综合 | 在线视频欧美精品 | 日韩国产精品久久 | 免费在线成人av | 亚洲一级黄色av | 免费瑟瑟网站 | 91在线入口| 91插插插免费视频 | 久久高清毛片 | 四虎永久免费 | 青春草视频在线播放 | 九九九在线观看 | 中文字幕在线免费观看视频 | 欧美成人精品欧美一级乱黄 | 伊人亚洲综合网 | 夜夜干夜夜 | 91在线视频观看 | 成人在线你懂得 | 三级av在线 | 91秒拍国产福利一区 | 日韩精品视频免费 | 午夜色大片在线观看 | 精品国产乱码久久久久久久 | 精品久久久久_ | www国产在线 | 久久精品视频播放 | 免费高清看电视网站 | 久久久久成人精品 | 热精品| 久久无码精品一区二区三区 | 天堂在线一区二区三区 | 国产精品成久久久久三级 | 成人午夜黄色 | 九九久久久久99精品 | www.超碰97.com | 午夜 在线| 午夜av网站| 欧美日韩三级在线观看 | 国产一级免费在线观看 | 久草在线视频看看 | 超碰在线中文字幕 | 在线播放国产一区二区三区 | 国产一级做a | 日韩字幕在线观看 | 亚州国产精品 | 成人午夜影院在线观看 | 国产成人av在线影院 | 五月婷婷国产 | 免费观看www7722午夜电影 | 人人看看人人 | 激情综合网色播五月 | 伊人五月天| 亚洲精品电影在线 | 一区二区三区高清在线观看 | 国产一区二区三区高清播放 | 99国产一区二区三精品乱码 | 色国产视频 | 免费日韩一区二区 | 天堂av色婷婷一区二区三区 | 精品福利在线观看 | 91香蕉视频黄 | 亚洲高清在线视频 | 久久国产精品免费视频 | 免费视频91蜜桃 | 午夜婷婷在线观看 | 久久精品这里热有精品 | 亚洲高清在线 | 国产色资源 | 黄色免费在线视频 | 免费成人结看片 | 久久伦理电影网 | 最近中文字幕第一页 | 国产精品久久久久久久久久了 | 成人在线观看日韩 | 在线国产99 | 在线免费高清视频 | 免费看片网页 | 国产成人av电影在线观看 | 亚洲精品黄 | 不卡电影免费在线播放一区 | a级国产乱理论片在线观看 特级毛片在线观看 | 久久99深爱久久99精品 | 亚洲成熟女人毛片在线 | 99在线播放| 99热这里只有精品久久 | 欧美成人播放 | 国产精品久久久久久麻豆一区 | 丰满少妇在线观看资源站 | 99久久婷婷国产综合亚洲 | 久久免费毛片 | 992tv成人免费看片 | 99精品欧美一区二区三区黑人哦 | 国产精品久久久久av | 日韩欧美69 | 国产在线观看免费av | 激情五月在线 | 91麻豆网 | 亚洲精品小视频 | 欧美一区二区日韩一区二区 | 永久免费av在线播放 | 在线观看成人国产 | 久久久久久久久久久久久9999 | 久久人人插 | 欧美日韩高清不卡 | 国产一级高清 | 日韩欧美视频在线观看免费 | 爱干视频| 在线观看视频国产一区 | 中文字幕免费高清在线观看 | 久久亚洲福利 | 丁香高清视频在线看看 | 国产超碰在线 | 久久久精品久久 | 久久精品美女视频网站 | 成人中文字幕+乱码+中文字幕 | 久久人人爽人人片av | 一区二区三区免费在线播放 | 激情婷婷| 欧美午夜精品久久久久久孕妇 | 在线免费观看的av网站 | 中文字幕av免费 | 亚洲三级毛片 | 91人人视频在线观看 | 亚洲3级| a级国产片 | 久久五月婷婷综合 | 日本性久久 | 国产一二区在线观看 | 亚洲日本黄色 | 欧美一区二区三区在线看 | 黄色av观看 | 粉嫩av一区二区三区四区 | 狠狠色丁香久久综合网 | 二区三区中文字幕 | 日韩高清免费观看 | 激情喷水 | 久草免费在线观看 | 国产福利91精品一区 | 午夜久久久久久久久久久 | 久久黄色a级片 | 97国产精品亚洲精品 | 国产精品一区二区久久 | 五月婷婷免费 | 女人18精品一区二区三区 | 天天摸天天舔天天操 | 黄av免费| 91在线看| 91久久精品日日躁夜夜躁国产 | 久久五月婷婷丁香 | 亚洲国产视频直播 | 精品国内自产拍在线观看视频 | 久久久久久久久免费视频 | 91精品国产综合久久久久久久 | 日本精品一区二区三区在线播放视频 | 九九天堂| 国产激情免费 | 色先锋资源网 | 国产男女无遮挡猛进猛出在线观看 | 欧美视屏一区二区 | 国产1区2区3区精品美女 | 日韩免费专区 | 免费精品国产va自在自线 | 在线看一区二区 | 黄色小说在线免费观看 | 日韩高清不卡一区二区三区 | 日韩欧美高清免费 | 中文字幕资源网在线观看 | av成人在线看 | 91久久偷偷做嫩草影院 | 久久综合精品一区 | 西西大胆免费视频 | 久操中文字幕在线观看 | 国产九九热 | 国内精品久久久久久久影视麻豆 | 亚洲天堂网在线视频观看 | 中文av一区二区 | 日韩欧美69| 福利区在线观看 | 一区在线观看 | 曰本三级在线 | 国产一区 在线播放 | 在线播放日韩av | 91在线精品秘密一区二区 | 在线免费黄色av | 国产亚洲一区二区在线观看 | 91桃色视频 | 天天av资源 | 首页av在线| 99热超碰在线 | 免费看三级黄色片 | 日本午夜在线观看 | 亚洲日本在线一区 | 国产精品久久久久久模特 | 韩日av在线 | 精品99在线视频 | 99热这里只有精品国产首页 | 日韩在线观看第一页 | 久草精品国产 | 亚洲伊人第一页 | 日韩在线观看精品 | 国产色视频一区 | 天堂在线v| 久久久影院官网 | 九九热视频在线播放 | 国产一级久久久 | 久久夜夜操 | 欧美性脚交 | 国产精品视频大全 | 成人免费网视频 | 久久久久久久久久国产精品 | 亚洲视频在线视频 | 国产精品成人aaaaa网站 | 在线免费国产视频 | 免费高清在线视频一区· | 丁香婷婷网 | 美女视频黄免费网站 | 91日本在线播放 | 免费网站黄| 亚洲最大在线视频 | 日日干天天插 | av丝袜制服 | 欧美a√大片 | 99re热精品视频 | 国产精品毛片一区视频 | 成人毛片在线观看 | 五月婷婷视频在线观看 | 99精品视频中文字幕 | 国产伦理久久精品久久久久_ | 6080yy精品一区二区三区 | 亚洲视频在线视频 | 探花视频免费观看高清视频 | 亚洲天天| 国产在线中文 | 午夜影视一区 | 久久久免费 | 日本 在线 视频 中文 有码 | 在线观看成人av | 亚洲精品国产精品乱码在线观看 | 9999精品| 久久精品国产一区二区三 | 黄网在线免费观看 | 天天综合久久 | 成人免费观看av | 欧美精品在线观看免费 | 欧美久久久一区二区三区 | 日韩高清不卡一区二区三区 | 国产一卡久久电影永久 | 少妇精品久久久一区二区免费 | 91九色丨porny丨丰满6 | 久久精品网站视频 | 狠狠狠色丁香婷婷综合久久五月 | 美女视频黄免费 | 国产精品一区二区三区99 | 日本一区二区三区视频在线播放 | 韩国一区在线 | 亚洲禁18久人片 | 狠狠综合网 | 美女网站视频免费都是黄 | 99精品视频网站 | 免费a一级| 成年人在线观看网站 | 日韩精品久久久免费观看夜色 | 99久久精品国产一区二区成人 | 日韩免费电影在线观看 | 日韩高清在线一区二区三区 | 欧美精品国产综合久久 | 国产精品久久久久一区二区国产 | 91伊人久久大香线蕉蜜芽人口 | 99视频在线精品免费观看2 | 欧美伦理电影一区二区 | 在线观看网站你懂的 | 毛片永久免费 | 在线免费观看国产黄色 | a午夜电影 | 在线视频免费观看 | 黄色免费网站下载 | 日韩免费在线观看视频 | 偷拍精偷拍精品欧洲亚洲网站 | 深爱综合网 | 久久久精品视频网站 | 中文字幕免费看 | 成人少妇影院yyyy | 日韩剧情 | 超碰免费久久 | 亚州视频在线 | 日韩在线电影一区 | 一区二区在线影院 | 日日躁夜夜躁aaaaxxxx | 亚洲综合成人av | 蜜臀久久99精品久久久无需会员 | 免费观看成人网 | 91精品国产91久久久久久三级 | 麻豆观看 | 高清视频一区二区三区 | 免费在线黄色av | 国产成人久久av | 在线观看午夜av | 精品国产资源 | 日韩黄色在线观看 | 在线观看免费国产小视频 | 中文字幕在线网址 | 久久久久久久电影 | 91日韩精品 | 久久久午夜电影 | 美女精品 | 国产精品国产亚洲精品看不卡15 | 日韩av午夜在线观看 | 久久免费播放视频 | 男女视频91| 麻豆国产视频下载 | 91麻豆精品国产 | 成年人黄色免费看 | 久草9视频| 精品国模一区二区三区 | 国产精品二区在线 | 一本一道久久a久久综合蜜桃 | 国产九九九精品视频 | 久久伊人国产精品 | 粉嫩av一区二区三区入口 | 四虎国产永久在线精品 | 在线观看免费视频 | 天天插夜夜操 | 日本高清中文字幕有码在线 | 午夜精品区 | 激情综合网婷婷 | 日本久久久久久久久 | 久久看片 | www.久热| 久久综合九色综合97婷婷女人 | 麻豆极品| 一区精品在线 | 在线观看精品黄av片免费 | 日本在线观看黄色 | 久久综合九色综合久99 | 手机在线看永久av片免费 | 99热九九这里只有精品10 | 亚洲成人av电影在线 | 97超碰在线免费观看 | 91超在线 | 日韩精品中文字幕久久臀 | 久在线观看 | 黄a在线| 天天色天天操天天爽 | 国产精品九九久久99视频 | 天天综合在线观看 | 中文字幕 国产精品 | 免费91麻豆精品国产自产在线观看 | 亚洲天堂网在线观看视频 | 少妇性aaaaaaaaa视频 | 91原创在线观看 | 日韩一级片网址 | 久久久久久久久影视 | 国产成人在线免费观看 | 成人影片在线播放 | 国产精品免费在线播放 | 国产精品精品国产婷婷这里av | 国产精品第10页 | 国产一级黄色片免费看 | 成人91av | 黄色精品一区 | 国产免费久久久久 | 国产伦精品一区二区三区高清 | 精品国产三级a∨在线欧美 免费一级片在线观看 | 久草免费色站 | 综合色综合 | 久久精品综合 | 中文字幕 成人 | 亚洲综合日韩在线 | 麻豆久久一区 | 成年人免费av网站 | 久久久久成人精品亚洲国产 | 久久久这里有精品 | 欧美日韩国产精品久久 | 亚洲精品视频一二三 | 成人h视频在线 | 特黄特色特刺激视频免费播放 | 国内精品久久久久久久久久清纯 | 国产在线最新 | 精品久久久久久久久久久久久久久久 | 亚洲一区二区三区在线看 | 天天干天天干天天干天天干天天干天天干 | 国产麻豆精品免费视频 | 久久99深爱久久99精品 | 久久,天天综合 | 免费观看v片在线观看 | 久久伊人色综合 | 国产高清无av久久 | 五月综合激情网 | 91超碰在线播放 | 在线观看黄av | 日本公妇色中文字幕 | 亚洲精品av中文字幕在线在线 | 国产精品久久电影观看 | 在线观看成人一级片 | 六月丁香六月婷婷 | 99精品热视频只有精品10 | 国产 日韩 在线 亚洲 字幕 中文 | 亚洲最大在线视频 | www看片网站 | 911国产在线观看 | 2019中文最近的2019中文在线 | 在线中文日韩 | 欧美精品一区在线发布 | 人人澡人摸人人添学生av | av免费播放| 亚洲国产丝袜在线观看 | 久久综合日 | 99久免费精品视频在线观看 | 久久成人在线视频 | 国产精品嫩草55av | 天天爱综合 | 欧美嫩草影院 | 欧美aaa视频 | 日韩欧美精品在线 | 久久视频在线观看免费 | 久久免费视频在线观看30 | 丁香婷婷久久久综合精品国产 | 中日韩免费视频 | 波多野结衣网址 | 四虎影视8848aamm | 国产中文字幕在线观看 | 一区二区欧美在线观看 | 欧美国产一区在线 | 亚洲电影久久久 | 伊人五月综合 | 国产精久久久久久妇女av | 91视频91蝌蚪 | 久久成人国产精品一区二区 | 色偷偷网站视频 | 国产高清视频免费在线观看 | 国产精品自在欧美一区 | 欧美日韩一区二区免费在线观看 | 欧美一级片在线播放 | 国产一级黄大片 | 久久成电影 | 黄色av影院 | 久久 地址 | 天天操人人要 | 综合精品久久久 | 中文字幕日韩国产 | 亚洲一区av | 有没有在线观看av | 久久国产片 | 美女黄频在线观看 | 17videosex性欧美| 91看片淫黄大片一级在线观看 | 激情五月激情综合网 | 91久久精品一区二区三区 | 天天操夜夜想 | 免费欧美精品 | 色网站免费在线观看 | 欧美日韩视频一区二区 | 国产精品美女视频网站 | 色99网| 成人午夜在线观看 | 国产精成人品免费观看 | 精品亚洲一区二区 | 天天操夜夜想 | 天天视频色 | 五月天激情开心 | 成人av电影在线播放 | 欧美精品久久久久久久久免 | 在线91精品 | 日韩免费电影一区二区三区 | 日本不卡一区二区三区在线观看 | 精品视频免费播放 | 国产99久久久国产 | 成人久久精品视频 | 视频在线观看亚洲 | 日韩久久视频 | 中文字幕韩在线第一页 | 久久这里有 | 一本一本久久a久久 | 在线视频91| 国产日本亚洲 | 婷婷午夜 | www.成人sex| 国产视频在线观看一区二区 | 久久久久亚洲国产精品 | 精品亚洲va在线va天堂资源站 | 成人午夜剧场在线观看 | 久久久久久久毛片 | 欧美一级看片 | 九九视频免费在线观看 | 伊人婷婷 | 精品久久久久久久久久久院品网 | japanese黑人亚洲人4k | 久久精品一| 国产91免费看 | 最新av观看 | 亚洲欧美日韩精品久久久 | 在线视频观看你懂的 | 亚洲五月 | 黄色a在线观看 | 久久国产麻豆 | 国产成人精品一区二区三区网站观看 | 麻豆传媒视频在线 | 国产在线观看xxx | 久久影院亚洲 | 一区二区三区四区五区在线 | 黄色三级久久 | 天天操天天操天天操天天操天天操 | 国产精品粉嫩 | www.夜夜草| 欧美综合色在线图区 | 九九精品视频在线观看 | 91精品视频网站 | 国产又黄又爽又猛视频日本 | 夜夜高潮夜夜爽国产伦精品 | 亚洲国产wwwccc36天堂 | 9ⅰ精品久久久久久久久中文字幕 | 激情视频综合网 | 三日本三级少妇三级99 | 福利视频一二区 | 91亚色在线观看 | 超碰97国产 | 亚洲视频,欧洲视频 | 国产精品久久久久久爽爽爽 | 91片黄在线观 | 午夜国产成人 | 久久久久久久久综合 | 久久国产一区二区 | 欧美另类z0zx | 手机在线视频福利 | 中文字幕乱码亚洲精品一区 | 中文字幕色在线 | 天天草夜夜 | 91在线蜜桃臀 | 久久国产综合视频 | 久久久激情视频 | 国际精品久久久久 | av网在线观看 | 伊人午夜 | 国产美女免费看 | 96国产精品视频 | 天堂网一区二区 | 91精品蜜桃 | 免费国产ww| 国产黄色a | 麻豆成人在线观看 | 日韩精品最新在线观看 | 国模精品一区二区三区 | 成人a免费视频 | 国产九色在线播放九色 | 久久手机视频 | 成人av一区二区在线观看 | 精品女同一区二区三区在线观看 | 国产99久久久久久免费看 | 亚洲成a人片在线www | 又黄又爽的免费高潮视频 | 一级成人在线 | 亚洲一区日韩在线 | 天天做天天爱天天综合网 | 超碰av在线 | 超碰人人在线观看 | 午夜视频在线观看欧美 | 欧美成天堂网地址 | 午夜国产一区二区三区四区 | 一区中文字幕 | 永久黄网站色视频免费观看w | 国产亚洲片 | 日韩一区二区久久 | 免费高清看电视网站 | 成人久久久久久久久久 | 永久免费精品视频网站 | 天天躁日日躁狠狠躁av中文 | 国产精品久久久久av免费 | 久久国产a| 网站在线观看你们懂的 | 激情视频在线观看网址 | 96亚洲精品久久久蜜桃 | 激情婷婷六月 | avcom在线| 91在线91拍拍在线91 | 综合久色| 2019精品手机国产品在线 | 欧美黑人性猛交 | a级国产片 | 国产精品国产三级在线专区 | 久久优| 欧美日韩免费观看一区二区三区 | 天天躁天天躁天天躁婷 | 国产精品毛片久久 | 久久1电影院 | 天天干,夜夜爽 | 国产精品一区二区三区在线免费观看 | 超碰伊人网 | 丁香婷五月 | 国产又粗又猛又色 | 视频一区二区国产 | 久久久久高清毛片一级 | 日韩小视频 | 91精品中文字幕 | 色婷婷免费视频 | 国产中文字幕视频在线观看 | 色五月色开心色婷婷色丁香 | 亚洲一级黄色片 | 99久久精品视频免费 | 在线三级播放 | 69久久99精品久久久久婷婷 | 九九免费在线观看 | 欧美va天堂在线电影 | 精品国产欧美一区二区三区不卡 | 欧美在线aa | 日韩在线免费小视频 | 欧美激情综合五月色丁香 | 久久精品国产免费 | 天天操狠狠操网站 | 亚洲精品xxxx | 国产精品成人av久久 | 免费观看一级特黄欧美大片 | 最新色视频 | 亚洲h在线播放在线观看h | 高潮久久久 | 狠狠色丁婷婷日日 | 国产精品自产拍在线观看桃花 | 中文字幕国产视频 | 九九热99视频 | 色综合亚洲精品激情狠狠 | 国产成人一二片 | 久久免费的精品国产v∧ | 日韩精品一区二区久久 | 日韩一级精品 | 欧美污污视频 | 久久综合国产伦精品免费 | 91视频3p| 亚洲黄色影院 | 国产日韩精品一区二区三区在线 | 国产成人精品av在线观 | 婷婷激情综合五月天 | 久久人人爽爽 | 欧美成人亚洲 | 色视频在线看 | 欧美一级小视频 | 精品国产电影一区 | 久久伊人精品天天 | 91大神免费视频 | 亚洲欧美日韩国产一区二区 | 91亚洲精品在线 | 久久精品亚洲综合专区 | 免费看黄在线观看 | 久久与婷婷 | 五月婷婷中文 | 久久久综合香蕉尹人综合网 | 婷婷网站天天婷婷网站 | 国产精品网站一区二区三区 | 91成年视频 | 久草com| 久久夜色精品国产亚洲aⅴ 91chinesexxx | 欧美视频99 | 人人干人人做 | 久久99操 | 中文字幕一区二区三区在线观看 | 99精品免费在线观看 | 99国产精品一区二区 | 久久99精品波多结衣一区 | 97国产精品免费 | 精品主播网红福利资源观看 | 中文在线免费观看 | 国产在线精品视频 | 五月天六月丁香 | 视频91在线 | 国产中文| 日韩美精品视频 | 久久另类视频 | 福利区在线观看 | 狠狠色伊人亚洲综合网站色 | 四虎影视久久久 | 成人性生爱a∨ | 伊人五月 | 国产99色| 国产三级久久久 | 婷婷丁香久久五月婷婷 | 国产在线观看你懂得 | 超碰97在线看 | 福利区在线观看 | av在线超碰| 天堂av一区二区 | 久久 国产一区 | 亚洲男人天堂2018 | 国产黄色理论片 | 九色一区二区 | 在线免费av观看 | 国产亚洲在线 | 特级毛片在线 | 国产一区视频在线观看免费 | 久久久久9999亚洲精品 | 国产精品高潮久久av | 国产视频色 | 911久久| 91成人天堂久久成人 | 亚洲理论片在线观看 | 最新日韩电影 | 免费看毛片在线 | 日韩三级视频在线看 | 久99久在线视频 | 国产在线更新 | 久久视频国产精品免费视频在线 | 精品视频中文字幕 | 男女拍拍免费视频 | 黄色精品久久 | 成人激情开心网 | 日韩精品免费在线播放 | 久久网站av | 日本三级国产 | 国产一级片毛片 | 欧美成人精品三级在线观看播放 | 国产精品不卡在线播放 | 日韩va亚洲va欧美va久久 | 婷婷五天天在线视频 | 91成人观看 | 九九九免费视频 | 国产一区国产二区在线观看 | 久久人人看 | 国产精品久99 | 91福利视频免费 | 国产成人久久精品一区二区三区 |