欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解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是一種類類型。對(duì)于類型A來說,在它聲明之后定義之前,它是一個(gè)不完整類型,編譯器僅僅知道A是一個(gè)類類型,但是A類到底有哪些成員,到底占用了多少空間是無從得知的。不完整類型也是無法創(chuàng)建其對(duì)象的。

一個(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類對(duì)象的成員,其定義依賴于完整的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.對(duì)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類對(duì)象的成員,其定義依賴于完整的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;
}

讓我們?cè)侔焉厦娴某绦蜇S富一下,內(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類對(duì)象的成員,其定義依賴于完整的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)可以相對(duì)自由,規(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類的對(duì)象,將doFunc聲明為靜態(tài)函數(shù)就可以讓我們不創(chuàng)建類的對(duì)象,直接通過類去調(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ù)對(duì)定義和聲明的依賴性沒有影響,類內(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è)該類的對(duì)象。

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ì)相對(duì)的再麻煩一點(diǎn),不過只要按方法規(guī)劃好程序的組織結(jié)構(gòu),合理安排頭文件順序,也并不困難。

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

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

總結(jié)

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

相關(guān)文章

最新評(píng)論