国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

用html5做的網(wǎng)站素材山東seo百度推廣

用html5做的網(wǎng)站素材,山東seo百度推廣,ui需要學(xué)哪些內(nèi)容,開(kāi)發(fā)php網(wǎng)站建設(shè)目錄 1.類的6個(gè)默認(rèn)成員函數(shù) 2. 構(gòu)造函數(shù) 2.1 構(gòu)造函數(shù)的概念 2.2 構(gòu)造函數(shù)的特性 3. 析構(gòu)函數(shù) 3.1 析構(gòu)函數(shù)的概念 3.2 析構(gòu)函數(shù)的特性 4.拷貝構(gòu)造函數(shù) 4.1 拷貝構(gòu)造函數(shù)的概念 4.2 拷貝構(gòu)造函數(shù)的特性 5.賦值運(yùn)算符重載函數(shù) 5.1運(yùn)算符重載函數(shù) 5.2 賦值運(yùn)算符重…

目錄

1.類的6個(gè)默認(rèn)成員函數(shù)?

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

2.1 構(gòu)造函數(shù)的概念

2.2 構(gòu)造函數(shù)的特性

3. 析構(gòu)函數(shù)

3.1 析構(gòu)函數(shù)的概念

3.2 析構(gòu)函數(shù)的特性

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

4.1 拷貝構(gòu)造函數(shù)的概念

4.2 拷貝構(gòu)造函數(shù)的特性

5.賦值運(yùn)算符重載函數(shù)

5.1運(yùn)算符重載函數(shù)

5.2 賦值運(yùn)算符重載函數(shù)

?5.3?前置++和后置++重載

前置++:

后置++:

6.(&)取地址操作符重載函數(shù)和const取地址操作符重載函數(shù)


1.類的6個(gè)默認(rèn)成員函數(shù)?

如果一個(gè)類中什么成員都沒(méi)有,簡(jiǎn)稱為空類??墒强疹愔姓娴氖裁炊紱](méi)有嗎?

其實(shí)并不是的,任何類在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員函數(shù)。 默認(rèn)成員函數(shù):用戶沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱為默認(rèn)成員函數(shù)。

10cc6ae830cc460293c48d89a2fc6c29.png

類的6個(gè)默認(rèn)成員函數(shù)編譯器都會(huì)自己生成,如果編譯器生成的默認(rèn)成員函數(shù)能夠滿足我們的需求,我們就無(wú)需再自己實(shí)現(xiàn);

相反,如果編譯器生成的默認(rèn)成員函數(shù)不能滿足我們的需求,我們就必須要自己實(shí)現(xiàn)了。

本篇博客正是介紹類的這6個(gè)默認(rèn)成員函數(shù)都有哪些特性,講述什么情況下只需使用默認(rèn)成員函數(shù),什么情況下需要自己實(shí)現(xiàn)以及要怎樣實(shí)現(xiàn)的問(wèn)題!

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

2.1 構(gòu)造函數(shù)的概念

如下代碼,我們定義一個(gè)日期類并且調(diào)用成員函數(shù):

#include <iostream>
using namespace std;class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Data d1;d1.Init(2023, 5, 23);d1.Print();Data d2;d2.Init(2022, 5, 23);d2.Print();return 0;
}

?44e1ffcedc9c467aadcdcaf42e7d37aa.png

按照我們之前學(xué)過(guò)的,按部就班地先調(diào)用初始化成員函數(shù),再調(diào)用打印成員函數(shù),運(yùn)行結(jié)果也中規(guī)中矩地跑出來(lái)了。

可是有一天,我需要很多個(gè)Data變量,寫(xiě)代碼又太急躁,在創(chuàng)建某個(gè)Data變量時(shí)忘記調(diào)用Init成員函數(shù)了,如下所示:

#include <iostream>
using namespace std;class Data
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Data d1;d1.Init(2023, 5, 23);d1.Print();Data d2;d2.Print();return 0;
}

結(jié)果d2出現(xiàn)了隨機(jī)值:

1e18b406df5f416aaf074c75e432021d.png

通過(guò)以上贅述:對(duì)于Data類,可以通過(guò)Init公有方法給對(duì)象設(shè)置日期,如果忘記一次初始化就會(huì)導(dǎo)致bug的產(chǎn)生,那就不得不每次創(chuàng)建對(duì)象時(shí)都調(diào)用該方法設(shè)置信息,可是這樣是不是有些太麻煩了呢?有沒(méi)有方法在對(duì)象創(chuàng)建時(shí),就將對(duì)象設(shè)置進(jìn)去呢?

答案當(dāng)然是有的,這就引出了C++的1個(gè)默認(rèn)成員函數(shù)——構(gòu)造函數(shù):

構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次。

2.2 構(gòu)造函數(shù)的特性

構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)沒(méi)有用我們經(jīng)常熟悉的Init來(lái)命名,雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任 務(wù)并不是開(kāi)空間創(chuàng)建對(duì)象,而是初始化對(duì)象。

其特征如下:

1. 函數(shù)名與類名相同;

2. 無(wú)返回值;

3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù);

4. 構(gòu)造函數(shù)可以重載;

驗(yàn)證如下:

#include <iostream>
using namespace std;class Data
{
public://退出歷史舞臺(tái):/*void Init(int year, int month, int day){_year = year;_month = month;_day = day;}*///1. 函數(shù)名與類名相同;2. 無(wú)返回值;Data(int year, int month, int day){_year = year;_month = month;_day = day;}//4. 構(gòu)造函數(shù)可以重載;Data(){_year = 8;_month = 8;_day = 8;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{//調(diào)用含參構(gòu)造函數(shù):Data d1(2023, 5, 23); //3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù);d1.Print();//調(diào)用無(wú)參構(gòu)造函數(shù):Data d2;   //注意這里不能用諸如:Data d2();不能加(),因?yàn)闀?huì)與函數(shù)聲明產(chǎn)生歧義;d2.Print();return 0;
}

0ec1ea8c46d54512a7b8f4b376ae0b7b.png

當(dāng)然這里也完全可以用到缺省參數(shù):

#include <iostream>
using namespace std;class Data
{
public:Data(int year = 8, int month = 8, int day = 8){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Data d1(2023, 5, 23); d1.Print();Data d2(2023, 5);d2.Print();Data d3;d3.Print();return 0;
}

27c42a08303444c2a7c7c03cd85b2fb5.png

5.?如果類中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成。

代碼驗(yàn)證如下:

#include <iostream>
using namespace std;class Data
{
public:如果顯示定義,編譯器將不再生成//Data(int year, int month, int day)//{//	_year = year;//	_month = month;//	_day = day;//}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year ;int _month ;int _day ;
};int main()
{Data d1;d1.Print();return 0;
}

將Date類中構(gòu)造函數(shù)屏蔽后,代碼可以通過(guò)編譯,因?yàn)榫幾g器生成了一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù):

a623d63f50a747b791ab574543849818.png

將Date類中構(gòu)造函數(shù)放開(kāi),代碼編譯失敗,因?yàn)橐坏╋@式定義任何構(gòu)造函數(shù),編譯器將不再生成無(wú)參構(gòu)造函數(shù),放開(kāi)后報(bào)錯(cuò):error C2512: “Date”: 沒(méi)有合適的默認(rèn)構(gòu)造函數(shù)可用:

830c8ed1f42a4d059858ba4bc84f8d85.png


這時(shí)你可能要問(wèn)了:

在不顯示定義構(gòu)造函數(shù)的情況下,編譯器會(huì)生成默認(rèn)的構(gòu)造函數(shù)。但是看起來(lái)默認(rèn)構(gòu)造函數(shù)似乎并沒(méi)有什么用處呀!?

上面d1對(duì)象調(diào)用了編譯器生成的默認(rèn)構(gòu)造函數(shù),但是d1的對(duì)象_year/_month/_day,結(jié)果顯示依舊是隨機(jī)值,上面的運(yùn)行結(jié)果就是鐵錚錚的事實(shí)呀!這不是恰恰證明了這里編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒(méi)有什么卵用嗎?

這就涉及到了構(gòu)造函數(shù)的第6個(gè)特性:

6.C++把類型分成內(nèi)置類型(基本類型)和自定義類型。內(nèi)置類型就是語(yǔ)言提供的數(shù)據(jù)類型,如:int/char...,自定義類型就是我們使用class/struct/union等自己定義的類型,編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對(duì)自定義類型成員調(diào)用的它的默認(rèn)成員函數(shù),而內(nèi)置類型則不做處理。

代碼驗(yàn)證如下:

#include <iostream>
using namespace std;class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{public:
void Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
private:// 基本類型(內(nèi)置類型)int _year;int _month;int _day;// 自定義類型Time _t;
};
int main()
{Date d;d.Print();return 0;
}

2c14a15d9be9457d8eddcce7c59bddbb.png

說(shuō)到這里,我又有些不解,同樣都是變量,為什么還要分自定義類型調(diào)用它的默認(rèn)成員函數(shù),內(nèi)置類型卻不做處理呢?這難道不是一件畫(huà)蛇添足的事情嗎?

這次不否定了,說(shuō)的確實(shí)有道理,所以在C++11 中針對(duì)內(nèi)置類型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值。

代碼驗(yàn)證如下:

#include <iostream>
using namespace std;
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:// 基本類型(內(nèi)置類型)// C++11支持,聲明時(shí)給缺省值int _year = 2023;int _month = 5;int _day = 23;// 自定義類型Time _t;
};
int main()
{Date d;d.Print();return 0;
}

3120721a56154ceaa8705d2cb793f407.png


思考如下代碼能否正常運(yùn)行:

#include <iostream>
using namespace std;
class Date
{
public:Date(){_year = 2023;_month = 5;_day = 23;}Date(int year = 2023, int month = 5, int day = 23){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
// 以下測(cè)試函數(shù)能正常運(yùn)行嗎?
void Test()
{Date d1;
}

答案是否定的:

d656dc64cf62454db57c305c43dbf8e2.png

針對(duì)以上現(xiàn)象,可以引出構(gòu)造函數(shù)的第7個(gè)特性:

7.無(wú)參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。 注意:無(wú)參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒(méi)寫(xiě)編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)構(gòu)造函數(shù)。?

3. 析構(gòu)函數(shù)

3.1 析構(gòu)函數(shù)的概念

通過(guò)上面構(gòu)造函數(shù)的學(xué)習(xí),我們知道一個(gè)對(duì)象是怎么來(lái)的,那一個(gè)對(duì)象又是怎么沒(méi)的呢?這就需要我們學(xué)習(xí)析構(gòu)函數(shù)了:

析構(gòu)函數(shù):與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷毀,局部對(duì)象銷毀工作是由編譯器完成的。而對(duì)象在銷毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。

3.2 析構(gòu)函數(shù)的特性

析構(gòu)函數(shù)也是特殊的成員函數(shù),其特征如下:

1. 析構(gòu)函數(shù)名是在類名前加上字符 ~;

2. 無(wú)參數(shù)無(wú)返回值類型;

3. 一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:與構(gòu)造函數(shù)不同的是,析構(gòu)函數(shù)不能重載;

4. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù);

#include <iostream>
using namespace std;class Data
{
public:Data(int year = 2023, int month = 5, int day = 23){_year = year;_month = month;_day = day;}void Ptint(){cout << _year << "-" << _month << "-" << _day << endl;}//1. 析構(gòu)函數(shù)名是在類名前加上字符 ~;2. 無(wú)參數(shù)無(wú)返回值類型;~Data(){cout << "~Data" << endl;}private:int _year;int _month;int _day;
};
int main()
{Data d;d.Ptint();//這里為調(diào)用~Data,4. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù);return 0;
}

8a52bdecb899413c8864f56c085965be.png

當(dāng)然,Data類并不需要析構(gòu)函數(shù),這里只是為了證明C++自動(dòng)調(diào)用了析構(gòu)函數(shù)。

我們將析構(gòu)函數(shù)用到順序表中,可能會(huì)對(duì)析構(gòu)函數(shù)有更深刻的理解:

#include <iostream>
using namespace std;typedef int DataType;class SeqList
{
public:SeqList(){cout << "已經(jīng)調(diào)用了SeqList()構(gòu)造函數(shù);" << endl;_a = (DataType*)malloc(sizeof(DataType) * 4);if (_a == nullptr){perror("malloc failed");//如果擴(kuò)容失敗,說(shuō)明原因exit(-1);}_size = 0;//當(dāng)size≥capacity時(shí)就動(dòng)態(tài)開(kāi)辟空間_capacity = 4;//初始化數(shù)組容量為4}~SeqList(){cout << "已經(jīng)調(diào)用了~SeqList()析構(gòu)函數(shù);" << endl;free(_a);_a = nullptr;_size = _capacity = 0;}private:int* _a;int _size;int _capacity;
};int main()
{SeqList sl;return 0;
}

5bc4ac37324a40f48897bdce2e1c6215.png


對(duì)于第3條特性,系統(tǒng)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù),會(huì)不會(huì)完成一些事情呢?

5. 答案與構(gòu)造函數(shù)相似,編譯器生成的默認(rèn)析構(gòu)函數(shù),對(duì)自定義類型成員調(diào)用它的析構(gòu)函數(shù),而內(nèi)置類型則不做處理。

代碼驗(yàn)證如下:

#include <iostream>
using namespace std;class Time
{
public:~Time(){cout << "已經(jīng)調(diào)用了~Time()析構(gòu)函數(shù)" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內(nèi)置類型)int _year = 2023;int _month = 5;int _day = 23;// 自定義類型Time _t;
};
int main()
{Date d;return 0;
}

運(yùn)行結(jié)果為:

c3aa8ce8ea1245e68bad3d05be46c610.png

對(duì)以上結(jié)果和第3、第5條特性的詳細(xì)解釋:

程序運(yùn)行結(jié)束后輸出:“已經(jīng)調(diào)用了~Time()析構(gòu)函數(shù)”,在main中根本沒(méi)有直接創(chuàng)建Time類的對(duì)象,為什么最后會(huì)調(diào)用Time類的析構(gòu)函數(shù)?

因?yàn)?#xff1a;main中創(chuàng)建了Date對(duì)象d,而d中包含4個(gè)成員變量,其中_year, _month, _day三個(gè)是內(nèi)置類型成員,銷毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;

而_t是Time類對(duì)象,所以在d銷毀時(shí),要將其內(nèi)部包含的Time類的_t對(duì)象銷毀,所以要調(diào)用Time類的析構(gòu)函數(shù);

但是:main函數(shù)中不能直接調(diào)用Time類的析構(gòu)函數(shù),實(shí)際要釋放的是Date類對(duì)象,所以編譯器會(huì)調(diào)用Date類的析構(gòu)函數(shù),而Date沒(méi)有顯式提供,則編譯器會(huì)給Date類生成一個(gè)默認(rèn)的析構(gòu)函數(shù),目的是在其內(nèi)部調(diào)用Time類的析構(gòu)函數(shù);

即:當(dāng)Date對(duì)象銷毀時(shí),要保證其內(nèi)部每個(gè)自定義對(duì)象都可以正確銷毀,main函數(shù)中并沒(méi)有直接調(diào)用Time類析構(gòu)函數(shù),而是顯式調(diào)用編譯器為Date類生成的默認(rèn)析構(gòu)函數(shù);

注意:創(chuàng)建哪個(gè)類的對(duì)象則調(diào)用該類的構(gòu)造函數(shù),銷毀哪個(gè)類的對(duì)象則調(diào)用該類的析構(gòu)函數(shù)

6. 如果類中沒(méi)有申請(qǐng)資源時(shí),析構(gòu)函數(shù)可以不寫(xiě),直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如 Date類;有資源申請(qǐng)時(shí),一定要寫(xiě),否則會(huì)造成資源泄漏,比如SeqList類。?

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

4.1 拷貝構(gòu)造函數(shù)的概念

電視劇中以及現(xiàn)實(shí)中,雙胞胎的例子不在少數(shù),我們甚至可以說(shuō)簡(jiǎn)直他們就是一個(gè)模子里刻出來(lái)的!那么,在創(chuàng)建對(duì)象時(shí),可否創(chuàng)建一個(gè)與已存在對(duì)象一某一樣的新對(duì)象呢?答案的肯定的。

拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類 類型對(duì)象的引用(一般常用const修飾),在用已存在的類 類型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。

4.2 拷貝構(gòu)造函數(shù)的特性

1. 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。即:拷貝構(gòu)造函數(shù)是一個(gè)特殊的構(gòu)造函數(shù);

2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類 類型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用;

代碼驗(yàn)證如下:

不考慮特性2,我們偏偏就要直接傳值調(diào)用:

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 2023, int month = 7, int day = 7){_year = year;_month = month;_day = day;}Date( Date d)   {_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(d1);d2.Print();return 0;
}

會(huì)發(fā)現(xiàn)程序報(bào)錯(cuò):

b31d40394d984383bd2ed4c7383d1dc6.png

這是為什么呢?答案就像特征2中所說(shuō)的,在此過(guò)程中引發(fā)了無(wú)窮遞歸調(diào)用:

當(dāng)我們直接傳值調(diào)用時(shí),會(huì)發(fā)生先傳值再調(diào)用拷貝構(gòu)造函數(shù)的情況,即:

1a006970791e4df69d80e6f568086dfa.png

所以正確應(yīng)該如特性2所說(shuō)的那樣:

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 2023, int month = 7, int day = 7){_year = year;_month = month;_day = day;}// Date( Date d)   // 錯(cuò)誤寫(xiě)法:編譯報(bào)錯(cuò),會(huì)引發(fā)無(wú)窮遞歸Date( Date& d)   // 正確寫(xiě)法{_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(d1);d2.Print();return 0;
}

c645327e9ba447eabf769d8e8016f1ca.png

那么在概念中又提到:(一般常用const修飾),這是為什么呢?

這是為了防止我們?cè)诙x拷貝構(gòu)造函數(shù)時(shí)寫(xiě)反了:

Date( Date& d)   {d._year = _year;d._month = _month;d._day = _day;}

那么運(yùn)行結(jié)果不但不會(huì)正確,反而會(huì)偷雞不成蝕把米:

e2ca2a2a701447fca1d4aece104a34ee.png

所以加上const,即使出現(xiàn)了這樣的低級(jí)錯(cuò)誤,編譯器就會(huì)報(bào)錯(cuò),我們也能及時(shí)發(fā)現(xiàn):

 Date( const Date& d)   {d._year = _year;d._month = _month;d._day = _day;}

869fcebb101943eab5ebba1c98d195c5.png

正確代碼:

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 2023, int month = 7, int day = 7){_year = year;_month = month;_day = day;}// Date( Date d)   // 錯(cuò)誤寫(xiě)法:編譯報(bào)錯(cuò),會(huì)引發(fā)無(wú)窮遞歸Date( const Date& d)   // 正確寫(xiě)法{_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(d1);d2.Print();return 0;
}

3.與構(gòu)造函數(shù)和析構(gòu)函數(shù)相似,若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)象按內(nèi)存存儲(chǔ)按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。

在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類型是按照字節(jié)方式直接拷貝的,而自定義類型是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的。

代碼驗(yàn)證如下:

#include <iostream>
using namespace std;
class Time
{
public:Time(){_hour = 8;_minute = 8;_second = 8;}Time(const Time& t){_hour = t._hour;_minute = t._minute;_second = t._second;cout << "已經(jīng)調(diào)用了!Time::Time(const Time&)" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:// 基本類型(內(nèi)置類型)int _year = 2023;int _month = 7;int _day = 7;// 自定義類型Time _t;
};
int main()
{Date d1;// 用已經(jīng)存在的d1拷貝構(gòu)造d2,此處會(huì)調(diào)用Date類的拷貝構(gòu)造函數(shù)// 但Date類并沒(méi)有顯式定義拷貝構(gòu)造函數(shù),則編譯器會(huì)給Date類生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)Date d2(d1);d2.Print();return 0;
}

66d29c0c52404b548d981f17b4d16f1d.png


與前面的構(gòu)造函數(shù)和析構(gòu)函數(shù)相似的問(wèn)題:編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,還需要自己顯式實(shí)現(xiàn)嗎?

當(dāng)然像上面的Data類這樣的類是沒(méi)必要的。那么像順序表之類的類呢?驗(yàn)證如下:

#include <iostream>
using namespace std;typedef int DataType;class SeqList
{
public:SeqList(){cout << "已經(jīng)調(diào)用了SeqList()構(gòu)造函數(shù);" << endl;_a = (DataType*)malloc(sizeof(DataType) * 4);if (_a == nullptr){perror("malloc failed");//如果擴(kuò)容失敗,說(shuō)明原因exit(-1);}_size = 0;//當(dāng)size≥capacity時(shí)就動(dòng)態(tài)開(kāi)辟空間_capacity = 4;//初始化數(shù)組容量為4}//打印void Print(){for (int i = 0; i < _size; i++){cout << _a[i] << endl;}}//尾插void PushBack(const DataType& x){_a[_size] = x;_size++;}~SeqList(){cout << "已經(jīng)調(diào)用了~SeqList()析構(gòu)函數(shù);" << endl;free(_a);_a = nullptr;_size = _capacity = 0;}private:int* _a;int _size;int _capacity;
};int main()
{SeqList sl1;sl1.PushBack(1);sl1.PushBack(2);sl1.PushBack(3);sl1.PushBack(4);sl1.Print();SeqList sl2(sl1);sl2.Print();return 0;
}

運(yùn)行結(jié)果如圖:

7e3a76d24fa24a6eb89a9fd691544b0c.png

我們可以看到,程序崩潰了!這是為什么呢?

打開(kāi)監(jiān)視窗口看一下sl1和sl2的內(nèi)存地址:

a665e2e2d22f42d2910c10bb75c0a469.png

發(fā)現(xiàn)二者的地址相同,所以我們就知道了:?

1.sl1對(duì)象調(diào)用構(gòu)造函數(shù)創(chuàng)建,在構(gòu)造函數(shù)中,申請(qǐng)了(_capacity)4個(gè)元素的空間,然后里面存儲(chǔ)了4個(gè)元素:1 2 3 4;

2. sl2對(duì)象使用sl1對(duì)象拷貝構(gòu)造,而SeqList類沒(méi)有顯示定義拷貝構(gòu)造函數(shù),則編譯器會(huì)給SeqList類生成一份默認(rèn)的拷貝構(gòu)造函數(shù),默認(rèn)拷貝構(gòu)造函數(shù)是按照值拷貝的,即將sl1中的內(nèi)容原封不動(dòng)地拷貝到sl2中。因此sl1與sl2指向了同一塊內(nèi)存空間;

3. 當(dāng)程序退出時(shí),sl2和sl1要銷毀。sl2先銷毀,sl2銷毀時(shí)調(diào)用析構(gòu)函數(shù),已經(jīng)將0x00b59580的空間釋放了,但是sl1并不知道,到sl1銷毀時(shí),會(huì)將0x00b59580的空間再釋放一次(正如3.2的第5條特性說(shuō)的那樣),一塊內(nèi)存空間多次釋放,必然會(huì)導(dǎo)致bug的產(chǎn)生。


現(xiàn)在我已經(jīng)知道原因了,那么正確的代碼應(yīng)該怎么寫(xiě)呢?這就需要用到深拷貝去解決(關(guān)于深拷貝后面會(huì)有詳解):

//自定義拷貝構(gòu)造函數(shù),不用編譯器默認(rèn)生成的(深拷貝)SeqList( const SeqList& sl){_a = (DataType*)malloc(sizeof(DataType) * 4);//我也開(kāi)辟一個(gè)空間if (_a == nullptr){perror("malloc failed");//如果擴(kuò)容失敗,說(shuō)明原因exit(-1);}memcpy(_a, sl._a, sizeof(int) * sl._capacity);_size = sl._size;_capacity = sl._capacity;}

32d8e2931faa4aba9c7fd30cce45486b.png

所以,我們應(yīng)該要明白:

4.類中如果沒(méi)有涉及資源申請(qǐng)時(shí),拷貝構(gòu)造函數(shù)是否寫(xiě)都可以;一旦涉及到資源申請(qǐng) 時(shí),則拷貝構(gòu)造函數(shù)是一定要寫(xiě)的,否則就是淺拷貝。

5.賦值運(yùn)算符重載函數(shù)

5.1運(yùn)算符重載函數(shù)

在學(xué)習(xí)賦值運(yùn)算符重載之前,我們先來(lái)了解一下運(yùn)算符重載:

通過(guò)上面的學(xué)習(xí),我們已經(jīng)知道了內(nèi)置類型和自定義類型的區(qū)別,思考這樣一個(gè)問(wèn)題:

顯而易見(jiàn),內(nèi)置類型對(duì)象可以直接用各種運(yùn)算符,內(nèi)置類型是語(yǔ)言自己定義的,編譯直接轉(zhuǎn)換成指令

舉個(gè)簡(jiǎn)單的例子,內(nèi)置類型的int類型2和1,編譯器可以輕松知道2>1;內(nèi)置類型的double類型2.2和1.1,編譯器輕松知道2.2>1.1,諸如此類......

那么問(wèn)題來(lái)了,我們通篇寫(xiě)的Data類對(duì)象,我這時(shí)候需要判斷2012年7月7日與2013年7月7日哪個(gè)日期更大,編譯器能直接判斷出2012年7月7日<2013年7月7日嗎?顯然是不能的!因?yàn)樽远x類型編譯器不支持直接轉(zhuǎn)換成指令。

那么這時(shí)候就需要我們自己寫(xiě)一個(gè)函數(shù)來(lái)實(shí)現(xiàn):

寫(xiě)一個(gè)大于比較函數(shù):

bool Greater(Data d1, Data d2)
{if (d1._year > d2._year){return true;}else if (d1._year == d2._year && d1._month > d2._month){return true;}else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day){return true;}return false;
}

再比如我寫(xiě)一個(gè)等于比較的函數(shù):

bool Equal(Data d1, Data d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}

運(yùn)行一下:

#include <iostream>
using namespace std;
class Data
{
public:Data(int year = 2012, int month = 7, int day = 7){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}void Ptint(){cout << _year << "-" << _month << "-" << _day << endl;}//private:int _year;int _month;int _day;
};//布爾類型(bool)用于表示真(true)和假(false)的值。
//它只有兩個(gè)取值:true 和 false,分別對(duì)應(yīng) 1 和 0 。
bool Greater(Data d1, Data d2)
{if (d1._year > d2._year){return true;}else if (d1._year == d2._year && d1._month > d2._month){return true;}else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day){return true;}return false;
}bool Equal(Data d1, Data d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}int main()
{Data d1(2013, 7, 7);Data d2(2012, 7, 7);cout << Greater(d1, d2) << endl;cout << Equal(d1, d2) << endl; return 0;
}

40c3044b923f4d26a6af88b477fb7d38.png

說(shuō)到這里,我們來(lái)說(shuō)一個(gè)題外話,關(guān)于函數(shù)的命名,其實(shí)在C語(yǔ)言中我們就遇到過(guò)很多了,一個(gè)函數(shù)命名就如同給自己的孩子取名字一樣,比如上面的判斷大于和判斷相等函數(shù),我能用Greater、Equal,為什么就不能用DaYu、DengYu,或者Compare1、Compare2,又或者func1、func2呢?

這些確實(shí)都是可以的呀!我創(chuàng)建的函數(shù),我樂(lè)意怎樣取名我就怎樣取名!

可是話說(shuō)回來(lái),你的孩子在你口中叫狗蛋兒、在老師口中叫張三,有一天你去開(kāi)家長(zhǎng)會(huì),老師問(wèn)你是誰(shuí)的家長(zhǎng),你說(shuō)你是狗蛋兒的家長(zhǎng),你這樣說(shuō)老師會(huì)知道張三就是狗蛋兒,狗蛋兒就是張三嗎?

話再說(shuō)回來(lái),你寫(xiě)的函數(shù)叫DaYu、DengYu,而你的同事要用這個(gè)函數(shù),你寫(xiě)的DaYu、DengYu,同事能知道這是個(gè)什么函數(shù)嗎?

所以,為了規(guī)避這種情況,增強(qiáng)代碼的可讀性,C++引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。

函數(shù)名字為:關(guān)鍵字operator后面接需要重載的運(yùn)算符符號(hào)。

函數(shù)原型:返回值類型?operator操作符(參數(shù)列表)

因此上述兩個(gè)函數(shù)就可以寫(xiě)為(更加規(guī)范,我們加上const和&):

//bool Greater(Data d1, Data d2)
bool operator>(const Data& d1, const Data& d2)
{if (d1._year > d2._year){return true;}else if (d1._year == d2._year && d1._month > d2._month){return true;}else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day){return true;}return false;
}//bool Equal(Data d1, Data d2)
bool operator == (const Data& d1, const Data& d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{Data d1(2013, 7, 7);Data d2(2012, 7, 7);/*cout << Greater(d1, d2) << endl;cout << Equal(d1, d2) << endl; */cout << operator > (d1, d2) << endl;cout << operator == (d1, d2) << endl;return 0;
}

09a4e81684514c94a4c1a3efa58519c0.png

以及為了令自定義類型更貼合與內(nèi)置類型一樣讓編譯器自己計(jì)算,直接轉(zhuǎn)為指令:

int main()
{Data d1(2013, 7, 7);Data d2(2012, 7, 7);/*cout << Greater(d1, d2) << endl;cout << Equal(d1, d2) << endl; *//*cout << operator > (d1, d2) << endl;cout << operator == (d1, d2) << endl;*/bool ret1 = d1 > d2;   //d1>d2嗎?是為1,否為0;bool ret2 = d1 == d2;  //d1=d2嗎?是為1,否為0;int a = 3 > 2;         //3>2嗎?是為1,否為0;int b = 3 == 2;        //3=2嗎?是為1,否為0;cout << ret1 << endl << ret2 << endl;cout << a << endl << b << endl;return 0;
}

6c14fcf3156e47f4be3a01e2d23d84ae.png


仔細(xì)觀察我們上面寫(xiě)的Data類,可以發(fā)現(xiàn)我把private注釋掉了,那現(xiàn)在我把注釋關(guān)掉:

c95e1a2b39d84902b7952dc26cc6ed78.png

成員變量變私有了該怎么辦呢?其實(shí)C++常用的解決方法是直接將函數(shù)放到類里面,因?yàn)轭惱锩婵梢噪S便訪問(wèn)private:

class Data
{
public:Data(int year = 2012, int month = 7, int day = 7){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}bool operator == (const Data& d1, const Data& d2){return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;}void Ptint(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};

可是這樣寫(xiě)發(fā)現(xiàn)還是編譯不通過(guò):

02d4b6013b5f4a4dad00c0501e5e4edb.png

它說(shuō)函數(shù)參數(shù)太多?!我放到類外面參數(shù)就不多,怎么放到類里面就多參數(shù)了呢?

對(duì)!因?yàn)榇嬖谝粋€(gè)隱含的this指針(詳細(xì)請(qǐng)看C++入門2——類與對(duì)象(1)中的3),所以這時(shí)就可以這樣修改:

class Data
{
public:Data(int year = 2012, int month = 7, int day = 7){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}bool operator>(const Data& d2){if (_year > d2._year){return true;}else if (_year == d2._year && _month > d2._month){return true;}else if (_year == d2._year && _month == d2._month && _day > d2._day){return true;}return false;}bool operator == ( const Data& d2){return _year == d2._year && _month == d2._month && _day == d2._day;}void Ptint(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};

那么在函數(shù)調(diào)用時(shí),編譯器就會(huì)幫我們負(fù)重前行:

int main()
{Data d1(2013, 7, 7);Data d2(2012, 7, 7);bool ret1 = d1 > d2;    //d1.operator>(d2)--->d1.operator>(&d1,d2)bool ret2 = d1 == d2;   //d1.operator==(d2)--->d1.operator==(&d1,d2)cout << ret1 << endl << ret2 << endl;return 0;
}

所以運(yùn)算符重載歸納有以下特點(diǎn):

1. 不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的操作符:比如operator@;

2. 重載操作符必須有一個(gè)類類型參數(shù);

3.?用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型+,不能改變其含義;

4.?作為類成員函數(shù)重載時(shí),其形參看起來(lái)比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this;

5.? .*? ? ::? ? ?sizeof? ? ??:? ? ?.? ??以上5個(gè)運(yùn)算符不能重載。

5.2 賦值運(yùn)算符重載函數(shù)

鋪了那么多前戲,終于來(lái)到我們要學(xué)習(xí)的賦值運(yùn)算符重載函數(shù)了:

我們知道,拷貝構(gòu)造就是將一個(gè)已經(jīng)初始化的變量A拷貝到未初始化的變量B中,

那么如果存在兩個(gè)都已經(jīng)初始化的變量A、B,我想把A的值拷貝到B,顯然就不能再用拷貝構(gòu)造了,要用到我們就要開(kāi)始講的賦值運(yùn)算符重載:

1. 賦值運(yùn)算符重載格式:

參數(shù)類型:const T&,傳遞引用可以提高傳參效率

返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值檢測(cè)是否自己給自己賦值

返回*this :要復(fù)合連續(xù)賦值的含義

(詳解精華都在代碼里):

#include <iostream>
using namespace std;
class Data
{
public:Data(int year = 2012, int month = 7, int day = 7){_year = year;_month = month;_day = day;}Data(const Data& d){_year = d._year;_month = d._month;_day = d._day;}//d1=d2;d1傳給this,d2傳給d//返回值應(yīng)該是什么類型呢?當(dāng)然是Data類型;應(yīng)該返回d1的地址,所以用&引用返回Data& operator=(const Data& d){//判斷是否為自己給自己賦值,&放到這里不是引用,是取地址://判斷d2的地址是否與d1地址相等if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;//返回d1的地址}void Ptint(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Data d1(2023, 7, 7);Data d2(2022, 8, 8);//拷貝構(gòu)造:一個(gè)已經(jīng)存在的對(duì)象去拷貝初始化另一個(gè)對(duì)象Data d3(d2);d1.Ptint();d2.Ptint();d3.Ptint();cout << endl;//賦值運(yùn)算符重載:兩個(gè)已經(jīng)存在的對(duì)象拷貝d1 = d2;//運(yùn)算自定義類型,就要用到運(yùn)算符重載operator=d1.Ptint();d2.Ptint();d3.Ptint();
}

bfc851358b7a4f188d2536f11d56f2a8.png


2. 賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù)

?原因:賦值運(yùn)算符如果不顯式實(shí)現(xiàn),編譯器會(huì)生成一個(gè)默認(rèn)的。此時(shí)用戶再在類外自己實(shí)現(xiàn) 一個(gè)全局的賦值運(yùn)算符重載,就和編譯器在類中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值 運(yùn)算符重載只能是類的成員函數(shù)。

3. 用戶沒(méi)有顯式實(shí)現(xiàn)時(shí),編譯器會(huì)生成一個(gè)默認(rèn)賦值運(yùn)算符重載,以值的方式逐字節(jié)拷貝。注意:內(nèi)置類型成員變量是直接賦值的,而自定義類型成員變量需要調(diào)用對(duì)應(yīng)類的賦值運(yùn)算符重載完成賦值。?

4.如果類中未涉及到資源管理,賦值運(yùn)算符是否實(shí)現(xiàn)都可以;一旦涉及到資源管理則必 須要實(shí)現(xiàn)。

這些特性與拷貝構(gòu)造函數(shù)有極大的相似性,這里不再過(guò)多贅述。

?5.3?前置++和后置++重載

在C語(yǔ)言中,我們已經(jīng)知道了:前置++,先加后用;后置++,先用后加 這樣的基本常識(shí)

那么我們學(xué)了運(yùn)算符重載,現(xiàn)在我要自定義類型Data前置++和后置++,要怎么定義和實(shí)現(xiàn)呢?

前置++:

前置++為先+1后使用;所以前置++的返回值應(yīng)該是返回+1之后的結(jié)果;

故前置++重載函數(shù)的實(shí)現(xiàn)為:

// 前置++:返回+1之后的結(jié)果Date& operator++()//返回d1,所以返回值類型當(dāng)然為Date類型{_day += 1;return *this;//this指向的對(duì)象函數(shù)結(jié)束后不會(huì)銷毀,故以&引用方式返回提高效率}

后置++:

后置++為先使用后+1;所以后置++的返回值應(yīng)該為+1之前的舊值,故需在實(shí)現(xiàn)時(shí)需要先將this保存一份,然后給this+1;

那么問(wèn)題來(lái)了,前置++和后置++都是一元運(yùn)算符,實(shí)現(xiàn)起來(lái)兩個(gè)函數(shù)名字相同,都是operator++,怎么才能區(qū)分這兩個(gè)函數(shù)呢?

為了讓前置++與后置++形成能正確重載,C++規(guī)定:后置++重載時(shí)多增加一個(gè)int類型的參數(shù),但調(diào)用函數(shù)時(shí)該參數(shù)不用傳遞,編譯器自動(dòng)傳遞

故后置++重載函數(shù)的實(shí)現(xiàn)為:

	Date operator++(int){Date temp(*this);// 先將this保存一份,然后給this + 1_day += 1;return temp;    // temp是臨時(shí)對(duì)象,出了作用域就會(huì)被銷毀,//因此只能以值的方式返回,不能返回引用}

驗(yàn)證:

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 2023, int month = 7, int day = 7){_year = year;_month = month;_day = day;}// 前置++:返回+1之后的結(jié)果Date& operator++()//返回d1,所以返回值類型當(dāng)然為Date類型{_day += 1;return *this;//this指向的對(duì)象函數(shù)結(jié)束后不會(huì)銷毀,故以&引用方式返回提高效率}Date operator++(int){Date temp(*this);// 先將this保存一份,然后給this + 1_day += 1;return temp;    // temp是臨時(shí)對(duì)象,出了作用域就會(huì)被銷毀,//因此只能以值的方式返回,不能返回引用}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(2022, 7, 7);d1 = ++d2;//前置++,編譯器默認(rèn)調(diào)用不帶(int)參數(shù)的operator++d1.Print();d1 = d2++;//后置++,編譯器默認(rèn)調(diào)用帶(int)參數(shù)的operator++d1.Print();return 0;
}

042923162f184534aa7a272a212b0bb8.png

小知識(shí):在C語(yǔ)言中前置++和后置++二者的效率高低可能并不明顯,但是在C++中,一般來(lái)說(shuō)前置++的效率要高于后置++


6.(&)取地址操作符重載函數(shù)和const取地址操作符重載函數(shù)

終于來(lái)到類的最后兩個(gè)默認(rèn)成員成員函數(shù)了,二者的形式為:

class Date
{
public://(&)取地址操作符重載Date* operator&(){return this;}//const(&)取地址操作符重載const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};

這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。

這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可,只有特殊情況,才需 要重載,比如想讓別人獲取到指定的內(nèi)容!所以這里就不再過(guò)多介紹這兩個(gè)默認(rèn)成員函數(shù)。

(本篇完)?

http://m.aloenet.com.cn/news/36821.html

相關(guān)文章:

  • 做網(wǎng)站的服務(wù)器怎么弄什么網(wǎng)站推廣比較好
  • 做優(yōu)惠券網(wǎng)站賺錢嗎seo整站怎么優(yōu)化
  • 在搜狐快站上做網(wǎng)站怎么跳轉(zhuǎn)我的百度賬號(hào)登錄
  • wordpress 電商網(wǎng)站百度首頁(yè) 百度一下
  • 口碑好的揚(yáng)州網(wǎng)站建設(shè)指數(shù)是什么
  • 淘寶客的api怎么做網(wǎng)站高端定制網(wǎng)站建設(shè)公司
  • 企術(shù)建站公司網(wǎng)站設(shè)計(jì)方案
  • wordpress插件放哪兒的獨(dú)立站seo是什么
  • 重慶整合營(yíng)銷網(wǎng)站建設(shè)百度百家自媒體平臺(tái)注冊(cè)
  • 網(wǎng)站建設(shè)和網(wǎng)站搭建哪個(gè)好合肥百度推廣優(yōu)化排名
  • 五金塑膠 技術(shù)支持 東莞網(wǎng)站建設(shè)百度競(jìng)價(jià)托管一月多少錢
  • 青海省城鄉(xiāng)建設(shè)廳網(wǎng)站寧德市人口
  • 做網(wǎng)站為什么圖片上傳不了整站優(yōu)化系統(tǒng)
  • 深圳網(wǎng)站建設(shè)公司地址產(chǎn)品推廣方法有哪些
  • 藁城專業(yè)網(wǎng)站建設(shè)百度商城購(gòu)物
  • 成都網(wǎng)站建設(shè) 創(chuàng)新互聯(lián)今日搜索排行榜
  • 重慶樓市最新消息seo用什么工具
  • wordpress move導(dǎo)入數(shù)據(jù)搜索引擎排名優(yōu)化
  • 建設(shè)網(wǎng)站去工信部備案需要什么資料網(wǎng)站seo的內(nèi)容是什么
  • 做返利網(wǎng)站能賺錢的免費(fèi)入駐的電商平臺(tái)
  • 網(wǎng)站建設(shè)常用問(wèn)題庫(kù)黑帽seo是什么
  • 門店管理系統(tǒng)有哪些寧波免費(fèi)seo在線優(yōu)化
  • 東莞模板網(wǎng)頁(yè)制作惠州seo網(wǎng)站排名
  • 橋梁畢業(yè)設(shè)計(jì)代做網(wǎng)站6個(gè)好用的bt種子搜索引擎
  • 網(wǎng)站建設(shè)的需要分析做網(wǎng)絡(luò)優(yōu)化的公司排名
  • 長(zhǎng)沙的企業(yè)網(wǎng)站建設(shè)陜西網(wǎng)站設(shè)計(jì)
  • DW做旅游網(wǎng)站模板正規(guī)拉新推廣平臺(tái)有哪些
  • 網(wǎng)站建設(shè)業(yè)務(wù)文案亞洲長(zhǎng)尾關(guān)鍵詞挖掘
  • 湛江論壇建站模板集客營(yíng)銷軟件官方網(wǎng)站
  • 商業(yè)網(wǎng)站建設(shè)案例課程圖片優(yōu)化是什么意思