friend_function 成員變數和函數的存儲 c++實現了封裝,數據和處理數據的操作(函數)是分開存儲的。 c++中的非靜態數據成員直接內含在類對象中,就像c語言的struct一樣。 成員函數並不會出現在對象中,而是作為類的一部分存儲在代碼段中,需要通過對象或對象指針進行調用。成員函數可以訪 ...
friend_function
成員變數和函數的存儲
c++實現了封裝
,數據
和處理數據的操作(函數)
是分開存儲的。
-
c++中的非靜態數據成員直接內含在類對象中,就像c語言的struct一樣。
-
成員函數並不會出現在對象中,而是作為類的一部分存儲在代碼段中,需要通過對象或對象指針進行調用。成員函數可以訪問類的所有成員變數和成員函數,包括私有成員,但不能直接訪問靜態變數,需要使用類名或對象來訪問。
-
每一個非內聯成員函數(non-inline member function)只會誕生一份函數實例.
class Regina02 {
public:
int a;
};
class Regina03 {
public:
int mA;
static int sB;
};
class Regina04 {
public:
void printMyClass() {
cout << "Regina04 void printMyClass()" << endl;
}
public:
int mA;
static int sB;
};
class Regina05 {
public:
void printMyClass() {
cout << "Regina05 void printMyClass()" << endl;
}
static void ShowMyClass() {
cout << "Regina05 static void ShowMyClass()" << endl;
}
在這四個類裡面,後面一個類分別比前面一個類多一個成分,為了證實C++類對象中的變數和函數是分開存儲,我們分別實例化四個對象列印大小
int main() {
Regina02 regina02;
Regina03 regina03;
Regina04 regina04;
Regina05 regina05;
cout << "Regina02:" << sizeof(regina02) << endl; //4
//靜態數據成員並不保存在類對象中
cout << "Regina03:" << sizeof(regina03) << endl; //4
//非靜態成員函數不保存在類對象中
cout << "Regina04:" << sizeof(regina04) << endl; //4
//靜態成員函數也不保存在類對象中
cout << "Regina05:" << sizeof(regina05) << endl; //4
return 0;
發現所有的類對象大小都一樣,對於靜態成員變數和靜態成員函數,它們並不屬於類的對象,而是屬於整個類本身。它們存儲在靜態數據區,而不是存儲在類的對象中。靜態成員變數在程式運行期間只有一份實體,不隨類的對象的創建而分配記憶體,而靜態成員函數也不依賴於特定的類對象。
this指針
通過上例我們知道,c++的數據和操作也是分開存儲,並且每一個非內聯成員函數(non-inline member function)只會誕生一份函數實例,也就是說多個同類型的對象會共用一塊代碼
那麼問題是:這一塊代碼是如何區分那個對象調用自己的呢?
c++通過提供特殊的對象指針,this指針,解決上述問題。this指針指向被調用的成員函數所屬的對象。
c++規定,this指針是隱含在對象成員函數內的一種指針。當一個對象被創建後,它的每一個成員函數都含有一個系統自動生成的隱含指針this,用以保存這個對象的地址,也就是說雖然我們沒有寫上this指針,編譯器在編譯的時候也是會加上的。因此this也稱為“指向本對象的指針”,this指針並不是對象的一部分,不會影響sizeof(對象)的結果。
this指針是C++實現封裝的一種機制,它將對象和該對象調用的成員函數連接在一起,在外部看來,每一個對象都擁有自己的函數成員。一般情況下,並不寫this,而是讓系統進行預設設置。
this指針使用
-
當形參和成員變數同名時,可用this指針來區分
-
在類的非靜態成員函數中返回對象本身,可使用return *this.
class Use_this {
private:
string name;
int age;
public:
/*當形參名字和成員變數名字一樣時,this指針可以區分*/
Use_this(string name, int age) {
// name = name; false
this->name = name;
this-> age = age;
}
//返回對象本身的引用
//重載賦值操作符
//其實也是兩個參數,其中隱藏了一個this指針
Use_this personAdd(Use_this& person) {
string newname = this->name + person.name;
int newage = person.age + this->age;
Use_this newperson(newname, newage);
return Use_this(newname, newage);
}
void showPerson() {
cout << "name: " << name << "\n" << "age: " << age << endl;
}
};
int main(){
Use_this person1 = Use_this("regina ", 13);
Use_this person2 = Use_this("love Ivanlee", 14);
person1.showPerson();
Use_this person3 = person1.personAdd(person2);
person3.showPerson();
return 0;
}
const
當 const
關鍵字用於修飾成員函數時,它表示該成員函數是一個常量成員函數,也稱為常成員函數。常成員函數有以下特點:
- 常成員函數不會修改對象的狀態:常成員函數承諾不會修改類的任何非靜態成員變數。這意味著在常成員函數內部,不能修改成員變數的值,除非該成員變數被聲明為
mutable
。 - 常成員函數可以讀取對象的狀態:常成員函數可以訪問和讀取類的所有成員變數,包括非
const
成員變數。 - 常成員函數可以調用其他常成員函數:常成員函數可以調用其他常成員函數,因為它們都遵守不修改對象狀態的承諾。
在 C++ 中,
mutable
是一個關鍵字,用於聲明類的成員變數可以在常成員函數中被修改。通常情況下,常成員函數(被const
修飾的成員函數)不能修改類的成員變數,因為它們承諾不會改變對象的狀態。然而,有時候我們可能需要在常成員函數中修改某些變數,這時就可以使用mutable
關鍵字。當一個成員變數被聲明為
mutable
時,即使在常成員函數中,該成員變數仍然可以被修改。這樣做的目的是為了允許在邏輯上不改變對象狀態的情況下,修改一些與對象狀態無關的數據。
在ida裡面寫代碼的時候就能看到,當我們定義好了const成員函數時,沒有定義過的mutable的變數是不可以修改的。
友元函數
友元函數存在的主要原因是為了提供更靈活的設計和實現方式,同時在某些特定情況下確實需要直接訪問類的私有成員或保護成員。以下是一些使用友元函數的情況和原因:
- 訪問私有成員:有時候在類的外部需要訪問類的私有成員數據,但又不希望通過公有成員函數來實現。這種情況下,可以使用友元函數來實現對私有成員的直接訪問,提高代碼的封裝性和安全性。
- 提高效率:有些操作可能需要直接訪問類的私有數據,如果通過公有介面來訪問可能會降低效率。通過友元函數可以避免頻繁調用公有介面,提高程式執行效率。
- 實現非成員函數:某些操作與類密切相關,但又不屬於類的成員函數的範疇,這時候可以使用友元函數來實現這樣的操作。
- 重載運算符:在C++中,一些運算符如
<<
、>>
、+
等是可以重載的,如果想要對類進行運算符重載,並且需要直接訪問類的私有數據,可以使用友元函數來實現。(後續會解釋) - 類之間的協作:有時候多個類之間需要相互訪問對方的私有成員,可以使用友元函數來實現類之間的協作。
友元語法
friend關鍵字只出現在聲明處
其他類、類成員函數、全局函數都可聲明為友元
友元函數不是類的成員,不帶this指針
友元函數可訪問對象任意成員屬性,包括私有屬性
#include<iostream>
using namespace std;
class Home;// 前向聲明 Myfriend 類
class Myfriend {
public:
void PlayinBedroom(Home& home);
void Eatindinningroom(Home& home);
};
class Home {
private:
string bedroom;
string dinningroom;
public:
string sittingroom;
Home() {
bedroom = "bedroom";
dinningroom = "dinning room";
}
friend void CleanMyhome(Home &home) {
cout << "友元全局函數訪問" << home.bedroom << endl;
}
//成員函數做友元函數
friend void Myfriend::PlayinBedroom(Home& home); //必須把函數內容寫在類外面
friend void Myfriend::Eatindinningroom(Home& home);//類內只做聲明
};
void Myfriend::PlayinBedroom(Home& home) {
std::cout << "我的朋友正在" << home.bedroom << "里玩" << std::endl;
}
void Myfriend::Eatindinningroom(Home& home) {
std::cout << "我的朋友正在" << home.dinningroom << "里吃飯" << std::endl;
}
int main() {
Home myHome;
CleanMyhome(myHome); // 調用全局友元函數
Myfriend myFriend;
myFriend.PlayinBedroom(myHome); // 調用 Myfriend 的成員函數作為友元函數
myFriend.Eatindinningroom(myHome); // 調用 Myfriend 的成員函數作為友元函數
return 0;
}
[友元類註意]
1.友元關係不能被繼承。
2.友元關係是單向的,類A是類B的朋友,但類B不一定是類A的朋友。
友元關係不具有傳遞性。類B是類A的朋友,類C是類B的朋友,但類C不一定是類A的朋友。
練習題
請編寫電視機類,電視機有開機和關機狀態,有音量,有頻道,提供音量操作的方法,頻道操作的方法。由於電視機只能逐一調整頻道,不能指定頻道,增加遙控類,遙控類除了擁有電視機已有的功能,再增加根據輸入調台功能。
首先我們要寫一下電視的功能
class TV {
friend class Remote;
private:
int mState; //電視狀態,開機,還是關機
int mVolume; //電視機音量
int mChannel; //電視頻道
public:
enum {On,Off};
enum {minVol, maxVol = 100};
enum{ minChannel = 0, maxChannel = 255};
TV() {
mState = Off;
mVolume = minVol;
mChannel = minChannel;
}
void TurnOnorOff() {
this->mState = (this->mState == Off ? On : Off);
}
//調高音量
void VolumeUp() {
if (this->mVolume >= maxVol) {
return;
}
this->mVolume++;
}
void VolumeDown() {
if (this->mVolume == minVol) {
return;
}
this->mVolume--;
}
//更換電視頻道
void ChannelUp() {
if (this->mChannel >= maxChannel) {
return;
}
this->mChannel++;
}
void ChannelDown() {
if (this->mChannel <= minChannel) {
return;
}
this->mChannel--;
}
//展示當前電視狀態信息
void ShowTeleState() {
cout << "當前電視" << (mState == On ? "已開機":"已關機") << endl;
if (mState == On) {
cout << "當前音量:" << mVolume << endl;
cout << "當前頻道:" << mChannel << endl;
}
cout << "-------------" << endl;
}
class Remote {
private:
TV tv;
public:
Remote(TV& tv) {
this->tv = tv;
}
void OnOrOff() {
tv.TurnOnorOff();
}
//調高音量
void VolumeUp() {
tv.VolumeUp();
}
//調低音量
void VolumeDown() {
tv.VolumeDown();
}
//更換電視頻道
void ChannelUp() {
tv.ChannelUp();
}
void ChannelDown() {
tv.ChannelDown();
}
//設置頻道 遙控新增功能
void setChannel(int chan) {
if (chan < tv.minChannel || chan > tv.maxChannel) {
return;
}
tv.mChannel = chan;
}
//展示電視信息
void showTV() {
tv.ShowTeleState();
}
};
//直接操作電視
void test01() {
TV television;
television.ShowTeleState();
television.TurnOnorOff(); //開機
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.ChannelUp(); //頻道+1
television.ChannelUp(); //頻道+1
television.ShowTeleState();
}//通過遙控操作電視
void test02() {
//創建電視
TV television;
//創建遙控
Remote remote(television);
remote.OnOrOff();
remote.ChannelUp();//頻道+1
remote.ChannelUp();//頻道+1
remote.ChannelUp();//頻道+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.showTV();
}
本文來自博客園,作者:ivanlee717,轉載請註明原文鏈接:https://www.cnblogs.com/ivanlee717/p/18025808