最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解C++類的成員函數(shù)做友元產(chǎn)生的循環(huán)依賴問題

 更新時(shí)間:2022年03月23日 10:12:09   作者:榛栗栗栗子  
這篇文章主要為大家詳細(xì)介紹了C++類的成員函數(shù)做友元產(chǎn)生的循環(huán)依賴問題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

類的成員函數(shù)做友元時(shí),極易產(chǎn)生循環(huán)依賴問題,導(dǎo)致程序無法編譯通過。何謂循環(huán)依賴,簡單舉個(gè)例子,A類的定義需要完整的B類,B類的定義又需要完整的A類,兩者相互依賴,都無法完成定義,這種現(xiàn)象便是循環(huán)依賴。在講解循環(huán)依賴問題之前,要先說一下類的聲明問題。

類的聲明

就像可以把函數(shù)的聲明和定義分離開一樣,我們也可以僅聲明類但暫時(shí)不定義它

class A;         //這是A類的聲明

這種聲明有時(shí)被稱為向前聲明,它向程序中引入了名字A并且指明了A是一種類類型。對于類型A來說,在它聲明之后定義之前,它是一個(gè)不完整類型,編譯器僅僅知道A是一個(gè)類類型,但是A類到底有哪些成員,到底占用了多少空間是無從得知的。不完整類型也是無法創(chuàng)建其對象的。

一個(gè)不完整類型能使用的情形的非常有限的,可以定義指向不完整類型的指針或引用,可以聲明(但不能定義)以不完整類型作為參數(shù)或者返回值類型的函數(shù)。

類的成員函數(shù)做友元以及可能產(chǎn)生的循環(huán)依賴問題

情況一:B類的成員函數(shù)func是A類的友元,且B類不依賴A類

首先說明,類A聲明類的B的某個(gè)成員函數(shù)為友元這一行為,已經(jīng)讓類A依賴于完整的類B。因?yàn)?,只有?dāng)類B定義完成,成為一個(gè)完整的類后,編譯器才能知道類B有哪些成員,才知道類B是否真的具有成員函數(shù)func。

這種情況并未形成循環(huán)依賴,但是但凡要將類的成員函數(shù)做友元,我們都必須組織規(guī)劃好程序的結(jié)構(gòu)以滿足聲明和定義的彼此依賴關(guān)系。我們需按照如下方式設(shè)計(jì)程序:

1.完成B類的定義,且成員函數(shù)func只能聲明,不能在類內(nèi)定義

2.完成A類的定義,包括成員函數(shù)func的友元聲明

3.在類外完成函數(shù)func的定義

實(shí)際上情況一較少出現(xiàn),B類的成員函數(shù)func已經(jīng)是A類的友元了,說明函數(shù)func有使用A類成員的意圖,但凡想使用A類的成員,就難免要依賴于不完整或是完整的A類。 

示例代碼和說明:

#include<iostream>
#include<string>
using namespace std;
class manage//定義manage類,完成定義后manage將成為完整的類
{
public:
	//printPerson函數(shù)的定義將使用person類對象的成員,其定義依賴于完整的person類,故此處不能定義,只能聲明,否則將產(chǎn)生循環(huán)依賴
	ostream& printPerson(ostream&)const;
};
class person//定義person類
{
	//聲明manage的成員函數(shù)printPerson為友元,需要完整的manage類,即manage類的定義
	friend ostream& manage::printPerson(ostream&)const;
public:
	person() = default;
	person(string name, unsigned int age) :m_name(name), m_age(age) {}
private:
	string m_name;
	unsigned int m_age = 0;
};
//成員函數(shù)printPerson的定義需要完整的person類
//實(shí)際上這是一個(gè)比較雞肋的函數(shù),并沒有什么實(shí)際意義,這里更多的只是為了展示情況一下該如何組織程序結(jié)構(gòu)
ostream& manage::printPerson(ostream& os)const
{
	person p("zhenlllz", 21);
	os << p.m_name << '\t' << p.m_age;
	return os;
}
int main()
{
	manage m;
	m.printPerson(cout) << endl;//結(jié)果為 “zhenlllz	21”
	system("pause");
	return 0;
}

情況二:類B的成員函數(shù)func成員函數(shù)是類A的友元,且B類依賴于不完整的A類

這種情況也并未形成循環(huán)依賴,同樣的,我們也需要組織規(guī)劃好程序的結(jié)構(gòu)。我們需按照如下方式設(shè)計(jì)程序:

1.對A類進(jìn)行聲明

2.完成B類的定義,且成員函數(shù)func只能聲明,不能在類內(nèi)定義

3.完成A類的定義,包括成員函數(shù)func的友元聲明

4.在類外完成函數(shù)func的定義

其實(shí)情況一和情況二的總體思路就是優(yōu)先完成依賴度低的類的定義,再依次完成依賴條件已達(dá)成的類或函數(shù)的定義。

示例代碼和說明:

#include<iostream>
#include<string>
using namespace std;
class person;//向前聲明person類,person類現(xiàn)在為不完整的類
class manage//定義manage類
{
public:
	//printPerson函數(shù)的聲明至少需要不完整的person類,即person類的聲明
	//printPerson函數(shù)的定義將使用person類對象的成員,其定義依賴于完整的person類,故此處不能定義,只能聲明,否則將產(chǎn)生循環(huán)依賴
	ostream& printPerson(ostream&, const person&)const;  
};
class person//定義person類
{
	//聲明manage的成員函數(shù)printPerson為友元需要完整的manage類,即manage類的定義
	friend ostream& manage::printPerson(ostream&, const person&)const;
public:
	person() = default;
	person(string name, unsigned int age) :m_name(name), m_age(age) {}
private:
	string m_name;
	unsigned int m_age = 0;
};
//成員函數(shù)printPerson的定義需要完整的person類
ostream& manage::printPerson(ostream& os, const person& p)const		
{
	os << p.m_name << '\t' << p.m_age;
	return os;
}
int main()
{
	person p("zhenlllz", 21);
	manage m;
	m.printPerson(cout, p) << endl;//結(jié)果為 “zhenlllz	21”
	system("pause");
	return 0;
}

讓我們再把上面的程序豐富一下,內(nèi)容更多,原理相同:

#include<iostream>
#include<string>
using namespace std;
class person;//向前聲明person類,person類現(xiàn)在為不完整的類
class manage//定義manage類
{
public:
	//printPerson函數(shù)的聲明至少需要不完整的person類,即person類的聲明
	//printPerson函數(shù)的定義將使用person類對象的成員,其定義依賴于完整的person類,故此處不能定義,只能聲明,否則將產(chǎn)生循環(huán)依賴
	ostream& printPerson(ostream&, const person&)const;  
};
class person//定義person類
{
	//聲明manage的成員函數(shù)printPerson為友元需要完整的manage類,即manage類的定義
	friend ostream& manage::printPerson(ostream&, const person&)const;
public:
	person() = default;
	person(string name, unsigned int age) :m_name(name), m_age(age) {}
private:
	string m_name;
	unsigned int m_age = 0;
};
//成員函數(shù)printPerson的定義需要完整的person類
ostream& manage::printPerson(ostream& os, const person& p)const		
{
	os << p.m_name << '\t' << p.m_age;
	return os;
}
int main()
{
	person p("zhenlllz", 21);
	manage m;
	m.printPerson(cout, p) << endl;//結(jié)果為 “zhenlllz	21”
	system("pause");
	return 0;
}

情況三:類B的成員函數(shù)func是類A的友元,且B類依賴于完整的A類

這種情況便形成了循環(huán)依賴,只依靠組織規(guī)劃程序的結(jié)構(gòu)已經(jīng)無解,一種較為有效且通用的解決辦法便是添加一個(gè)銜接過度的類Help。Help類的引入使得程序結(jié)構(gòu)可以相對自由,規(guī)劃程序結(jié)構(gòu)的思路是:

類和非成員函數(shù)的聲明不是必須在它們的友元聲明之前。當(dāng)一個(gè)名字第一次出現(xiàn)在一個(gè)友元聲明中時(shí),我們隱式地假設(shè)該名字在當(dāng)前作用域中是可見的,所以類做友元和非成員函數(shù)做友元沒有太多程序結(jié)構(gòu)上的限制,我們利用這一點(diǎn),加入一個(gè)過度的Help類有效幫助我們化解循環(huán)依賴問題。

在B類依賴于完整的A類的前提下,那么B類的定義只能在A類的后面,函數(shù)func不再可能聲明為A類的友元,函數(shù)func也就無法再使用A類的私有成員。讓Help類幫來搭建函數(shù)func和A類的橋梁,將Help類聲明為A類的友元,在Help類中添加函數(shù)func的實(shí)現(xiàn)手段即一個(gè)名為doFunc的靜態(tài)函數(shù),再讓B類聲明為Help的友元,Help類可以訪問A類的私有成員,而B類又可以訪問Help類的私有成員,B類間接訪問A類的途徑就形成了。

doFunc定義為靜態(tài)函數(shù)的原因在于,我們不希望類的使用者知道Help類的存在,更不希望去創(chuàng)建Help類的對象,將doFunc聲明為靜態(tài)函數(shù)就可以讓我們不創(chuàng)建類的對象,直接通過類去調(diào)用靜態(tài)成員函數(shù)。函數(shù)doFunc負(fù)責(zé)功能的實(shí)現(xiàn),而函數(shù)func則是接口,它負(fù)責(zé)傳遞參數(shù)調(diào)用doFunc。

推薦通過示例來了解進(jìn)一步了解,該示例和上一個(gè)示例的區(qū)別在于,m_v容器給予了類內(nèi)初始值,使得manage類必須依賴于完整的person類,形成了循環(huán)依賴。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
class person//person類的定義
{
	friend class Help;
public:
	person() = default;
	person(string name, unsigned int age) :m_name(name), m_age(age) {}
private:
	string m_name;
	unsigned int m_age = 0;
};
class Help
{
	friend class manage;
	using index = vector<person>::size_type;
    //manage類的成員函數(shù)change的實(shí)現(xiàn)
	static void doChange(person& p, string name, unsigned int age)
	{
		p.m_age = age;
		p.m_name = name;
	}
    //manage類的成員函數(shù)printPerson的實(shí)現(xiàn)
	static ostream& doPrintPerson(const person& p, ostream& os = cout)
	{
		os << p.m_name << '\t' << p.m_age;
		return os;
	}
};
class manage
{
public:
	using index = vector<person>::size_type;
	void add(const person& p) { m_v.push_back(p); } 
	inline void change(index, string, unsigned int); 
	inline void printPerson(index, ostream & = cout)const;	
	inline void printPerson(ostream & = cout)const; 
private:
	vector<person> m_v{ person("默認(rèn)",0) };
};
void manage::change(index i, string name, unsigned int age)
{
	if (i >= m_v.size())
		return;
	person& p = m_v[i];
	Help::doChange(p, name, age);
}
void manage::printPerson(index i, ostream& os)const
{
	if (i >= m_v.size())
		return;
	const person& p = m_v[i];
	Help::doPrintPerson(p, os) << endl;
}
void manage::printPerson(ostream& os)const
{
	for (auto p : m_v)
		Help::doPrintPerson(p, os) << endl;
}
int main()
{
	person p1("一號(hào)", 20);
	person p2("二號(hào)", 30);
	person p3("三號(hào)", 40);
	manage m;
	m.add(p1);
	m.add(p2);
	m.add(p3);
	m.change(2, "zhenlllz", 21);	
	m.printPerson(2, cout);
	m.printPerson();
	system("pause");
	return 0;
}

補(bǔ)充

1.內(nèi)聯(lián)函數(shù)與循環(huán)依賴問題

成員函數(shù)是否為內(nèi)聯(lián)函數(shù)對定義和聲明的依賴性沒有影響,類內(nèi)定義的成員函數(shù)是隱式內(nèi)聯(lián)的,我們也可以在函數(shù)聲明的返回類型前面加上 inline 使得該函數(shù)顯示的內(nèi)聯(lián)。將簡單函數(shù)聲明為內(nèi)聯(lián),可以提高程序的運(yùn)行效率,故示例程序中大部分成員函數(shù)都顯示或隱式的定義為了內(nèi)聯(lián)函數(shù)。

2.什么情況會(huì)需要類的聲明?什么情況又需要類的定義?

簡單來說,當(dāng)我們只需要知道有這么一個(gè)類存在時(shí),有類的聲明即可,比如定義該類的指針或引用,將該類作為函數(shù)聲明中的返回類型或者參數(shù);但我們需要知道類的具體內(nèi)容是什么,類的成員有哪些時(shí),就需要類的定義,比如要定義一個(gè)該類的對象。

3.《C++ Primer》一書 “友元再探” 小節(jié)的錯(cuò)誤

我正在學(xué)習(xí)該書,書本這里的錯(cuò)誤確實(shí)讓我苦惱了蠻久,這也是我寫下篇文章的原因之一。書本案例中的Screen類和Window_mgr類已經(jīng)形成了循環(huán)依賴,而書本卻指導(dǎo)用情況一的方案去解決該問題,顯然是行不通的。

4.沒列舉出來的情況(可以忽略這斷內(nèi)容)

還有一種更加雞肋的情況我沒有列舉出來,B類的成員函數(shù)func是A類的友元,B類不依賴A類,且函數(shù)func的定義中也未使用任何A類的成員。這種情況只需滿足B類的定義在A類定義之前,函數(shù)func的定義在B類的定義之后或是在類內(nèi)定義即可,程序的結(jié)構(gòu)是比較自由的。但問題在于,我都把func聲明為A類的友元了,卻不使用A類的成員,缺乏實(shí)際意義。

5.分文件編寫時(shí),注意頭文件聲明的順序

示例中并沒有進(jìn)行分文件編寫,分文件編寫會(huì)相對的再麻煩一點(diǎn),不過只要按方法規(guī)劃好程序的組織結(jié)構(gòu),合理安排頭文件順序,也并不困難。

6.更多細(xì)節(jié),要自己敲下代碼才能發(fā)覺

寫這篇文章的難度確實(shí)超過了我自己的預(yù)計(jì),越發(fā)思考?xì)w納,發(fā)現(xiàn)的細(xì)節(jié)問題越多,我也無法通過一文將細(xì)節(jié)問題一一說明。對這一塊困惑的話就自己舉幾個(gè)例子簡單練練吧,希望這篇文章對你有幫助。文章若有問題也請指正。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Qt物聯(lián)網(wǎng)管理平臺(tái)之實(shí)現(xiàn)告警短信轉(zhuǎn)發(fā)

    Qt物聯(lián)網(wǎng)管理平臺(tái)之實(shí)現(xiàn)告警短信轉(zhuǎn)發(fā)

    系統(tǒng)在運(yùn)行過程中,會(huì)實(shí)時(shí)采集設(shè)備的數(shù)據(jù),當(dāng)采集到的數(shù)據(jù)發(fā)生報(bào)警后,可以將報(bào)警信息以短信的形式發(fā)送給指定的管理員。本文將利用Qt實(shí)現(xiàn)告警短信轉(zhuǎn)發(fā),感興趣的可以嘗試一下
    2022-07-07
  • STL容器之vector源碼詳細(xì)解讀

    STL容器之vector源碼詳細(xì)解讀

    這篇文章主要介紹了STL容器之vector源碼詳細(xì)解讀,vector的數(shù)據(jù)安排和array和類似,它們的主要差別在于空間的運(yùn)用和靈活性,array是靜態(tài)空間,一旦配置了就不能改變,需要的朋友可以參考下
    2024-01-01
  • C語言示例講解while循環(huán)語句的用法

    C語言示例講解while循環(huán)語句的用法

    在不少實(shí)際問題中有許多具有規(guī)律性的重復(fù)操作,因此在程序中就需要重復(fù)執(zhí)行某些語句。一組被重復(fù)執(zhí)行的語句稱之為循環(huán)體,C語言while語句可以是單個(gè)語句,也可以是一個(gè)語句塊,其條件可以是任意表達(dá)式,true是任意非零值,當(dāng)條件為真時(shí),循環(huán)進(jìn)行迭代
    2022-06-06
  • shared_ptr線程安全性全面分析

    shared_ptr線程安全性全面分析

    正如boost文檔所宣稱的,boost為shared_ptr提供了與內(nèi)置類型同級(jí)別的線程安全性。這包括:1. 同一個(gè)shared_ptr對象可以被多線程同時(shí)讀取。2. 不同的shared_ptr對象可以被多線程同時(shí)修改成
    2013-09-09
  • 深入解析unsigned int 和 int

    深入解析unsigned int 和 int

    以下是對unsigned int和int進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-08-08
  • C語言實(shí)現(xiàn)貪吃蛇游戲設(shè)計(jì)

    C語言實(shí)現(xiàn)貪吃蛇游戲設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)貪吃蛇游戲設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C++中的函數(shù)知識(shí)點(diǎn)大全

    C++中的函數(shù)知識(shí)點(diǎn)大全

    這篇文章介紹了C++中的函數(shù)知識(shí)點(diǎn),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟

    Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟

    這篇文章主要介紹了Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C語言中設(shè)置用戶識(shí)別碼的相關(guān)函數(shù)的簡單講解

    C語言中設(shè)置用戶識(shí)別碼的相關(guān)函數(shù)的簡單講解

    這篇文章主要介紹了C語言中設(shè)置用戶識(shí)別碼的相關(guān)函數(shù)的簡單講解,包括setuid()函數(shù)和setreuid()函數(shù)以及setfsuid()函數(shù),需要的朋友可以參考下
    2015-08-08
  • 詳解C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理

    詳解C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理

    C++是一種面向?qū)ο蟮木幊陶Z言,在C++中,虛函數(shù)是實(shí)現(xiàn)多態(tài)性的關(guān)鍵。本文就來探討一下C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理及其在面向?qū)ο缶幊讨械膽?yīng)用吧
    2023-05-05

最新評(píng)論

怀仁县| 栖霞市| 长泰县| 台东市| 昭苏县| 军事| 宜丰县| 江口县| 县级市| 页游| 连云港市| 惠来县| 电白县| 武平县| 宁城县| 鄯善县| 达州市| 浮梁县| 丰宁| 濮阳市| 木兰县| 南郑县| 普安县| 襄城县| 酒泉市| 嘉鱼县| 丹凤县| 南雄市| 双鸭山市| 光泽县| 习水县| 海阳市| 福泉市| 静乐县| 厦门市| 璧山县| 东台市| 丰宁| 密山市| 孙吴县| 荆州市|