百度廣告平臺(tái)河北seo推廣
文章目錄
前言?
💬 歡迎討論:如果你在學(xué)習(xí)過程中有任何問題或想法,歡迎在評論區(qū)留言,我們一起交流學(xué)習(xí)。你的支持是我繼續(xù)創(chuàng)作的動(dòng)力!
👍 點(diǎn)贊、收藏與分享:覺得這篇文章對你有幫助!別忘了點(diǎn)贊、收藏并分享給更多的小伙伴哦!你們的支持是我不斷進(jìn)步的動(dòng)力!
🚀 分享給更多人:如果你覺得這篇文章對你有幫助,歡迎分享給更多對C++感興趣的朋友,讓我們一起進(jìn)步!
?4. 友元
在C++中,友元(friend)提供了一種突破類的訪問限定符的機(jī)制,使得外部函數(shù)或其他類可以訪問類的私有(private)和受保護(hù)的成員(protected)。友元可以是友元函數(shù)或友元類,而這種友元關(guān)系是在類定義中通過關(guān)鍵字?
friend
?顯式聲明的。
?4.1 友元涉及的基本概念
<一> 友元函數(shù):友元函數(shù)可以訪問類的私有和受保護(hù)成員,但它并不是類的成員函數(shù)。
<二>友元類:某個(gè)類的所有成員函數(shù)都可以是另一個(gè)類的友元,允許訪問該類的私有和受保護(hù)成員。
<三>單向關(guān)系:友元關(guān)系是單向的,如果A類是B類的友元,B類的成員函數(shù)可以訪問A類的私有成員,但A類不能訪問B類的私有成員,除非B類也顯式聲明A類為友元。
<四>友元的局限性:雖然友元提供了便利,但它打破了類的封裝性,增加了類之間的耦合,因此不宜濫用。
4.2 友元函數(shù)
友元函數(shù)是一個(gè)外部函數(shù),但通過友元聲明,它可以訪問類的私有和受保護(hù)的成員。友元函數(shù)不屬于類的成員函數(shù),它可以在類的任意地方聲明,而不受訪問限定符(
public
、private
、protected
)的限制。
示例:友元函數(shù)訪問兩個(gè)類的私有成員?
#include<iostream>
using namespace std;// 前置聲明,避免類A的友元函數(shù)不識(shí)別類B
class B;class A {// 友元聲明,允許函數(shù) func 訪問A類的私有成員friend void func(const A& aa, const B& bb);private:int _a1 = 1;int _a2 = 2;
};class B {// 友元聲明,允許函數(shù) func 訪問B類的私有成員friend void func(const A& aa, const B& bb);private:int _b1 = 3;int _b2 = 4;
};// 友元函數(shù)定義,能夠訪問A和B類的私有成員
void func(const A& aa, const B& bb) {cout << "A::_a1: " << aa._a1 << endl; // 訪問A類的私有成員cout << "B::_b1: " << bb._b1 << endl; // 訪問B類的私有成員
}int main() {A aa;B bb;func(aa, bb); // 調(diào)用友元函數(shù),訪問A和B類的私有成員return 0;
}
解釋:
- 函數(shù)?
func
?被聲明為?A
?和?B
?類的友元,因此它可以訪問?A
?類和?B
?類的私有成員變量?_a1
?和?_b1
。 - 雖然?
func
?是一個(gè)獨(dú)立于類的外部函數(shù),但通過友元聲明,它獲得了訪問類的私有數(shù)據(jù)的權(quán)限。
4.3 友元類
友元類允許一個(gè)類的所有成員函數(shù)訪問另一個(gè)類的私有和受保護(hù)成員。友元類的成員函數(shù)并不需要逐一聲明為友元,只要類被聲明為友元,所有的成員函數(shù)都能訪問另一個(gè)類的私有和受保護(hù)成員。
示例:友元類的使用?
#include<iostream>
using namespace std;class A {// 友元類B聲明,允許B類的所有成員函數(shù)訪問A類的私有成員friend class B;private:int _a1 = 1;int _a2 = 2;
};class B {
public:// 可以訪問A類的私有成員void func1(const A& aa) {cout << "A::_a1: " << aa._a1 << endl; // 訪問A類的私有成員cout << "B::_b1: " << _b1 << endl; // 訪問B類的私有成員}void func2(const A& aa) {cout << "A::_a2: " << aa._a2 << endl; // 訪問A類的私有成員cout << "B::_b2: " << _b2 << endl; // 訪問B類的私有成員}private:int _b1 = 3;int _b2 = 4;
};int main() {A aa;B bb;bb.func1(aa); // 通過B類的成員函數(shù)訪問A類的私有成員bb.func2(aa); // 通過B類的成員函數(shù)訪問A類的私有成員return 0;
}
解釋:
B
?類被聲明為?A
?類的友元類,因此?B
?類的所有成員函數(shù)都可以訪問?A
?類的私有成員?_a1
?和?_a2
。- 通過友元類聲明,不需要逐個(gè)將?
B
?類的成員函數(shù)聲明為?A
?類的友元,只要?B
?類是?A
?類的友元,B
?類的所有成員函數(shù)都可以訪問?A
?類的私有數(shù)據(jù)。
4.4 友元的特性與限制
- 單向關(guān)系:友元關(guān)系是單向的,如果?
A
?是?B
?的友元,那么?B
?類的成員可以訪問?A
?類的私有成員,但?A
?類不能訪問?B
?類的私有成員,除非?B
?類也將?A
?類聲明為友元。
示例:單向友元關(guān)系
class A;class B {friend class A; // B 聲明 A 為友元
private:int _b1 = 1;
};class A {
public:void accessB(B& bb) {// A 可以訪問 B 的私有成員cout << "B::_b1: " << bb._b1 << endl;}
};int main() {A aa;B bb;aa.accessB(bb); // A 類訪問 B 的私有成員return 0;
}
-
不具有傳遞性:友元關(guān)系不具有傳遞性。如果?
A
?是?B
?的友元,B
?是?C
?的友元,A
?不能訪問?C
?類的私有成員。 -
友元增加耦合性:雖然友元機(jī)制提供了訪問類私有成員的便利,但過度使用友元會(huì)導(dǎo)致類與類之間的耦合增加,破壞了類的封裝性。因此,友元不宜濫用,應(yīng)該謹(jǐn)慎使用。
4.5 友元函數(shù)與類的實(shí)際應(yīng)用
友元在某些情況下能提供方便,比如當(dāng)需要兩個(gè)類之間進(jìn)行緊密合作時(shí),使用友元可以簡化代碼,減少冗長的接口設(shè)計(jì)。
#include<iostream>
using namespace std;class Account;class Transaction {
public:void deposit(Account& account, double amount);void withdraw(Account& account, double amount);
};class Account {friend class Transaction; // 聲明 Transaction 類為友元類
public:Account(double balance) : _balance(balance) {}void showBalance() const {cout << "Balance: " << _balance << endl;}private:double _balance;
};void Transaction::deposit(Account& account, double amount) {account._balance += amount; // 直接訪問 Account 類的私有成員
}void Transaction::withdraw(Account& account, double amount) {if (amount <= account._balance) {account._balance -= amount;} else {cout << "Insufficient balance" << endl;}
}int main() {Account myAccount(1000.0);Transaction trans;trans.deposit(myAccount, 500.0); // 存款myAccount.showBalance(); // 輸出:1500trans.withdraw(myAccount, 200.0); // 取款myAccount.showBalance(); // 輸出:1300return 0;
}
解釋:
Transaction 類被聲明為 Account 類的友元類,因此 Transaction 類的成員函數(shù) deposit 和 withdraw 可以直接訪問 Account 類的私有成員 _balance。
這種情況下,友元機(jī)制簡化了類與類之間的合作,不必通過公共接口訪問私有數(shù)據(jù),減少了不必要的代碼冗。
總結(jié)
友元機(jī)制在C++中提供了一種打破類封裝的方式,允許外部函數(shù)或類訪問類的私有和受保護(hù)成員。它通過friend關(guān)鍵字來聲明友元函數(shù)或友元類,使得類之間的合作更加簡便。
友元函數(shù)和友元類都有其特定的用途,友元函數(shù)可以訪問多個(gè)類的私有成員,而友元類則使得另一個(gè)類的所有成員函數(shù)都可以訪問當(dāng)前類的私有數(shù)據(jù)。
友元關(guān)系是單向的,不具有傳遞性,過度使用友元會(huì)破壞類的封裝性和增加類的耦合性,應(yīng)該謹(jǐn)慎使用。
5. 內(nèi)部類
內(nèi)部類(Nested Class)是指一個(gè)類定義在另一個(gè)類的內(nèi)部。在C++中,內(nèi)部類和外部類是獨(dú)立的類,盡管它們之間有一定的聯(lián)系,但內(nèi)部類不屬于外部類的對象,它有自己的內(nèi)存布局和獨(dú)立性。使用內(nèi)部類通常是為了封裝和簡化類之間的關(guān)聯(lián)。
5.1 內(nèi)部類的基本概念
1.獨(dú)立性:盡管內(nèi)部類是定義在外部類的內(nèi)部,但它是一個(gè)獨(dú)立的類。外部類的對象并不包含內(nèi)部類的對象。也就是說,創(chuàng)建外部類的對象時(shí),并不會(huì)自動(dòng)創(chuàng)建內(nèi)部類的對象,內(nèi)部類需要單獨(dú)實(shí)例化。
2.友元關(guān)系:內(nèi)部類默認(rèn)是外部類的友元類,這意味著內(nèi)部類可以訪問外部類的私有成員。
3.封裝:使用內(nèi)部類可以將一些只在外部類內(nèi)部使用的邏輯封裝起來,使代碼更加緊湊和可控。內(nèi)部類可以定義在 private 或 protected 訪問限定符下,限制其他類對其的訪問。
5.2 內(nèi)部類的使用示例
#include<iostream>
using namespace std;class A {
private:static int _k; // 外部類的靜態(tài)成員int _h = 1; // 外部類的非靜態(tài)成員public:// 定義內(nèi)部類 Bclass B {public:// 內(nèi)部類方法可以訪問外部類的私有成員,因?yàn)?B 是 A 的友元類void foo(const A& a) {cout << "A::_k = " << _k << endl; // 訪問外部類的靜態(tài)成員cout << "A::_h = " << a._h << endl; // 訪問外部類的非靜態(tài)成員}};
};// 初始化外部類的靜態(tài)成員
int A::_k = 1;int main() {cout << "Size of A: " << sizeof(A) << endl; // 輸出 A 類的大小A::B b; // 創(chuàng)建內(nèi)部類 B 的對象A aa; // 創(chuàng)建外部類 A 的對象b.foo(aa); // 使用內(nèi)部類對象調(diào)用其方法,訪問外部類的私有成員return 0;
}
解釋:
內(nèi)部類 B 被定義在外部類 A 的 public 區(qū)域中,但它依然是 A 的友元類,可以訪問 A 類的私有成員變量 _k 和 _h。
創(chuàng)建了 A::B b 來實(shí)例化內(nèi)部類 B,然后通過內(nèi)部類的成員函數(shù) foo 訪問外部類對象的私有成員。
sizeof(A) 表示 A 類的大小,由于 A 只有一個(gè)整數(shù)成員 _h,因此其大小為4字節(jié)。
5.3 封裝與訪問權(quán)限
內(nèi)部類作為外部類的一部分,可以被放置在?
private
?或?protected
?訪問區(qū)域中,這樣可以控制內(nèi)部類的可見性。
示例:將內(nèi)部類放在?private
?區(qū)域?
#include<iostream>
using namespace std;class Outer {
private:class Inner { // 內(nèi)部類定義在 private 區(qū)域public:void display() {cout << "Inner class method called." << endl;}};public:void createInner() {Inner in; // 外部類的方法中可以創(chuàng)建內(nèi)部類的對象in.display();}
};int main() {Outer outer;outer.createInner(); // 通過外部類的方法調(diào)用內(nèi)部類的方法// Outer::Inner in; // 錯(cuò)誤!內(nèi)部類在 private 區(qū)域,外部無法訪問return 0;
}
解釋:
- 在這個(gè)例子中,內(nèi)部類?
Inner
?定義在?Outer
?類的?private
?區(qū)域,外部類的方法?createInner()
?可以創(chuàng)建?Inner
?類的對象并調(diào)用其方法。 - 嘗試在外部直接訪問?
Inner
?類會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)樗?private
?的。
5.4 內(nèi)部類的封裝與應(yīng)用場景
使用內(nèi)部類的一個(gè)常見場景是當(dāng)兩個(gè)類緊密相關(guān)時(shí),可以將一個(gè)類封裝到另一個(gè)類中。這樣做的目的是讓外部類管理內(nèi)部類的訪問,使得內(nèi)部類只為外部類所用。
場景:內(nèi)部類作為外部類的專屬工具類
#include<iostream>
using namespace std;class Manager {
private:class Task {public:void performTask() {cout << "Performing task." << endl;}};public:void assignTask() {Task t; // 外部類方法可以使用內(nèi)部類t.performTask();}
};int main() {Manager mgr;mgr.assignTask(); // 調(diào)用外部類的方法,執(zhí)行內(nèi)部類中的任務(wù)邏輯return 0;
}
解釋:
這里,Task 類被封裝在 Manager 類的 private 區(qū)域,表示 Task 只為 Manager 類服務(wù),外部無法直接訪問它。
這是一種封裝技術(shù),用于使 Task 類專屬于 Manager 類,外部無法創(chuàng)建 Task 對象,只能通過 Manager 類的方法來間接使用它。
5.5 內(nèi)部類的友元關(guān)系
內(nèi)部類默認(rèn)是外部類的友元類,這意味著內(nèi)部類可以訪問外部類的私有和受保護(hù)成員。這種設(shè)計(jì)允許內(nèi)部類和外部類之間進(jìn)行緊密的合作,使得內(nèi)部類可以像外部類的成員函數(shù)一樣訪問其內(nèi)部數(shù)據(jù)。
示例:內(nèi)部類訪問外部類的私有成員?
#include<iostream>
using namespace std;class Container {
private:int _data = 100;public:// 定義內(nèi)部類class Helper {public:void showData(const Container& c) {cout << "Container::_data = " << c._data << endl; // 訪問外部類的私有成員}};
};int main() {Container c;Container::Helper h; // 創(chuàng)建內(nèi)部類對象h.showData(c); // 調(diào)用內(nèi)部類的方法,訪問外部類的私有成員return 0;
}
解釋:
Helper
?類作為?Container
?的內(nèi)部類,默認(rèn)是?Container
?的友元,因此它可以訪問?Container
?類的私有成員?_data
。- 通過內(nèi)部類的對象?
h
,可以調(diào)用?showData
?方法來訪問外部類?Container
?的私有數(shù)據(jù)。
5.6 應(yīng)用:求 1 + 2 + 3 + … + n
#include<iostream>
using namespace std;class Solution {// 內(nèi)部類 Sum,用于進(jìn)行累加操作class Sum {public:Sum() {_ret += _i; // 每創(chuàng)建一個(gè)對象,累加一次當(dāng)前的 _i++_i; // 自增 i}};static int _i; // 用于計(jì)數(shù)的靜態(tài)變量static int _ret; // 用于存儲(chǔ)結(jié)果的靜態(tài)變量public:int Sum_Solution(int n) {Sum arr[n]; // 創(chuàng)建 n 個(gè) Sum 對象,觸發(fā)累加邏輯return _ret; // 返回累加的結(jié)果}
};// 初始化靜態(tài)變量
int Solution::_i = 1;
int Solution::_ret = 0;int main() {Solution sol;cout << "Sum of 1 to 5: " << sol.Sum_Solution(5) << endl; // 1 + 2 + 3 + 4 + 5 = 15return 0;
}
解釋:
- 內(nèi)部類?
Sum
?在創(chuàng)建對象時(shí)會(huì)自動(dòng)進(jìn)行累加操作,創(chuàng)建?n
?個(gè)?Sum
?對象等價(jià)于對?1
?到?n
?進(jìn)行
累加。
- 靜態(tài)變量?
_i
?用于記錄當(dāng)前的計(jì)數(shù),_ret
?用于存儲(chǔ)累加的結(jié)果。
總結(jié)
內(nèi)部類是一種封裝機(jī)制,允許將類定義在另一個(gè)類的內(nèi)部,從而限制內(nèi)部類的可見性或封裝內(nèi)部邏輯。內(nèi)部類與外部類獨(dú)立,但它默認(rèn)可以訪問外部類的私有成員。
內(nèi)部類的主要優(yōu)勢是封裝性和緊密耦合。當(dāng)一個(gè)類主要是為了另一個(gè)類服務(wù)時(shí),將其設(shè)計(jì)為內(nèi)部類可以減少外部依賴和接口冗余。
內(nèi)部類可以用于實(shí)現(xiàn)復(fù)雜的邏輯封裝、類間的緊密合作、計(jì)算封裝等多個(gè)場景,但應(yīng)謹(jǐn)慎使用,避免過度增加類的復(fù)雜性。
?6. 匿名對象
匿名對象是C++中的一種特殊對象,和普通的有名對象不同,匿名對象沒有名字,僅在表達(dá)式中被使用,生命周期非常短暫。它的生命周期只限于當(dāng)前語句,當(dāng)語句執(zhí)行結(jié)束后,匿名對象就會(huì)自動(dòng)被銷毀并調(diào)用析構(gòu)函數(shù)。匿名對象的典型用法是臨時(shí)定義對象,完成某項(xiàng)任務(wù)后立即銷毀。
6.1 匿名對象的基本概念
匿名對象的定義:匿名對象是通過直接調(diào)用構(gòu)造函數(shù)創(chuàng)建的對象,而沒有為其指定名字。形式上,它看起來像 A() 或 A(1) 這樣的表達(dá)式。
生命周期:匿名對象的生命周期非常短暫,只有在當(dāng)前表達(dá)式結(jié)束時(shí)存在,表達(dá)式執(zhí)行完畢后,匿名對象立即調(diào)用析構(gòu)函數(shù)被銷毀。
應(yīng)用場景:匿名對象通常用于臨時(shí)性操作,例如快速調(diào)用某個(gè)對象的成員函數(shù)或操作符,而不需要將該對象保存在變量中。
匿名對象 vs 有名對象
有名對象:對象名(實(shí)參)
例:B pro(10);(有名對象)
生命周期:與作用域相關(guān),當(dāng)作用域結(jié)束時(shí)對象銷毀。
匿名對象:類型(實(shí)參)
例:B(5);(匿名對象)
生命周期:只在當(dāng)前表達(dá)式有效,隨后立即銷毀。
6.2 匿名對象的創(chuàng)建與銷毀
在C++中,通過 B()
?或 B(10)
?這樣的語法直接調(diào)用構(gòu)造函數(shù)來創(chuàng)建匿名對象,匿名對象沒有名字,生命周期僅限于當(dāng)前行,結(jié)束后立即調(diào)用析構(gòu)函數(shù)進(jìn)行銷毀.
示例:
#include<iostream>
using namespace std;class A {
public:// 構(gòu)造函數(shù)A(int a = 0) : _a(a) {cout << "A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = " << _a << endl;}// 析構(gòu)函數(shù)~A() {cout << "~A() 析構(gòu)函數(shù)被調(diào)用, _a = " << _a << endl;}private:int _a;
};int main() {A aa1; // 有名對象 aa1 的創(chuàng)建// 不能這樣定義對象,因?yàn)榫幾g器無法確定是函數(shù)聲明還是對象定義// A aa1();// 創(chuàng)建匿名對象并立即銷毀A(); A(1); A aa2(2); // 有名對象 aa2 的創(chuàng)建,生命周期為整個(gè)作用域// 匿名對象用于調(diào)用函數(shù),完成任務(wù)后立即銷毀Solution().Sum_Solution(10);return 0;
}
輸出:
A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = 0
~A() 析構(gòu)函數(shù)被調(diào)用, _a = 0
A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = 1
~A() 析構(gòu)函數(shù)被調(diào)用, _a = 1
A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = 2
~A() 析構(gòu)函數(shù)被調(diào)用, _a = 2
解釋:
A()
?和?A(1)
?創(chuàng)建的是匿名對象,它們在當(dāng)前語句結(jié)束后立即調(diào)用析構(gòu)函數(shù)。- 有名對象?
aa1
?和?aa2
?是在整個(gè)作用域內(nèi)存在的,它們在作用域結(jié)束時(shí)調(diào)用析構(gòu)函數(shù)。 - 匿名對象?的使用場景之一是調(diào)用某個(gè)方法或操作符后立即銷毀,不占用額外的資源。
6.3 匿名對象的應(yīng)用場景
6.3.1 匿名對象用于臨時(shí)調(diào)用成員函數(shù)
匿名對象的一個(gè)常見應(yīng)用場景是用來臨時(shí)調(diào)用某個(gè)類的成員函數(shù),執(zhí)行完任務(wù)后不需要該對象的存在。
class Solution {
public:int Sum_Solution(int n) {return n * (n + 1) / 2;}
};int main() {// 使用匿名對象調(diào)用 Sum_Solution 函數(shù)int result = Solution().Sum_Solution(10); // 匿名對象創(chuàng)建后立即銷毀cout << "Sum of 1 to 10: " << result << endl;return 0;
}
解釋:
- 匿名對象?
Solution()
?被創(chuàng)建,用于調(diào)用?Sum_Solution
?函數(shù)。函數(shù)調(diào)用結(jié)束后,匿名對象立即銷毀,不再占用資源。 - 這是一種常見的設(shè)計(jì)模式,適用于不需要保存對象狀態(tài)的場景。
6.3.2 匿名對象避免對象命名
示例:返回匿名對象
class A {
public:A(int a) : _a(a) {cout << "A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = " << _a << endl;}~A() {cout << "~A() 析構(gòu)函數(shù)被調(diào)用, _a = " << _a << endl;}private:int _a;
};// 函數(shù)返回一個(gè)匿名對象
A createA() {return A(100); // 返回匿名對象
}int main() {createA(); // 調(diào)用 createA 函數(shù),返回的匿名對象立即銷毀return 0;
}
輸出:
A(int a) 構(gòu)造函數(shù)被調(diào)用, _a = 100
~A() 析構(gòu)函數(shù)被調(diào)用, _a = 100
解釋:
- 函數(shù)?
createA
?返回一個(gè)匿名對象,返回后立即銷毀。 - 匿名對象在不需要進(jìn)一步使用的情況下,能夠有效減少對象創(chuàng)建和銷毀的負(fù)擔(dān)。
6.4 匿名對象的注意事項(xiàng)
-
生命周期短暫:匿名對象的生命周期只在當(dāng)前語句結(jié)束時(shí)有效,不能跨語句使用匿名對象。如果需要在多行代碼中使用對象,必須創(chuàng)建有名對象。
錯(cuò)誤示例:
A obj = A(1); ?// 正確,有名對象 obj
A(1).foo(); ? ?// 匿名對象調(diào)用方法
// A(1); ? ? ? ?// 錯(cuò)誤:匿名對象無法在下一行使用
2.編譯器解析問題:在C++中,有些語法可能導(dǎo)致編譯器誤判為函數(shù)聲明而不是對象創(chuàng)建。因此,注意避免如下情況:
錯(cuò)誤示例:
A aa1(); ?// 被誤判為函數(shù)聲明,實(shí)際上不是對象的創(chuàng)建
正確用法:
A aa1(1); // 明確創(chuàng)建對象
?3.匿名對象的返回值優(yōu)化(RVO):現(xiàn)代C++編譯器通常會(huì)對匿名對象進(jìn)行優(yōu)化,在返回對象時(shí)避免多余的拷貝操作。這種優(yōu)化稱為返回值優(yōu)化(RVO)。
總結(jié)
- 匿名對象是沒有名字的臨時(shí)對象,生命周期非常短暫,通常用于一次性操作,如臨時(shí)調(diào)用成員函數(shù)或返回值。
- 匿名對象在表達(dá)式結(jié)束后立即調(diào)用析構(gòu)函數(shù)銷毀,適用于不需要持久化對象的場景。
- 匿名對象避免了額外的命名和管理開銷,在簡化代碼的同時(shí)提高了代碼的簡潔性和可讀性。
相信通過這篇文章你對C++類與對象高級(jí)部分的有了初步的了解。如果此篇文章對你學(xué)習(xí)C++有幫助,期待你的三連,你的支持就是我創(chuàng)作的動(dòng)力!!!
下一篇文章再會(huì).