C++ 的 static -靜態變數與函式的小實驗

複習上一篇:C++ 的 static -靜態變數與函式詳細說明
https://husking-studio.com/cpp-static/

前面提到, 靜態成員函式 只能存取 靜態成員變數。

那如果我真的想要用靜態成員函式 存取 一般成員變數呢?

※重要聲明:「用靜態成員函式存取一般成員變數」是研究與實驗性質的嘗試,是為了在研究過程中更了解「 靜態宣告 」的特性,實務上是非常不建議這樣使用的。

非常不建議的原因是,靜態成員函式本來就是為了存取靜態成員變數存在,而想存取一般成員變數,本來就有一般成員函式可以用了。所以「用靜態成員函式來存取一般成員變數」,不只繞了路,還會造成對「靜態」定義的混淆,沒有任何好處喔。

用一個指向自己的靜態指標可以嗎?再用靜態成員函式去拿自己,再拿自己的一般成員變數可以嗎?我們來試試看!

#include <iostream>
using namespace std;

class A
{
public:
	static int m_nNum;//宣告static member variable
	static A* m_pThis;//宣告一個指向自己的靜態指標
private:
	int m_nNum2;
	int m_nNum3;
public:
	A() { m_nNum2 = 10; m_nNum3 = 20; m_pThis = this;}
	static int GetNum2() { return m_pThis->m_nNum2; }
	static int StaticGetNum3() { return m_pThis->GetNum3(); }

	void SetNum3(int n) { m_nNum3 = n; }
	int GetNum3() { return m_nNum3; }
};

int A::m_nNum = 0;
A* A::m_pThis = 0;

int main()
{
	cout << "A::m_nNum = " << A::m_nNum << endl;
	//cout<<A::GetNum2()<<endl;	//這行是會當掉的,因為 m_pThis=0
	cout << endl;

	cout << "建立物件a1" << endl;
	A a1;
	cout << "A::m_nNum = " << A::m_nNum << endl;
	cout << "a1.m_nNum = " << a1.m_nNum << endl;
	cout << "a1.GetNum2() = " << a1.GetNum2() << endl;
	cout << "a1.StaticGetNum3() = " << a1.StaticGetNum3() << endl;
	cout << endl;

	system("pause");
	return 0;
}

執行結果

A::m_nNum = 0

建立物件a1
A::m_nNum = 0
a1.m_nNum = 0
a1.GetNum2() = 10
a1.StaticGetNum3() = 20

好像可以呢,可以用靜態成員函式 GetNum2() 取得 一般成員變數 m_nNum2,也可以用靜態成員函式 StaticGetNum3() 呼叫 一般成員函式 GetNum3()。

再新增一個物件看看

int main()
{
	cout << "建立物件a1" << endl;
	A a1;
	cout << "A::m_nNum = " << A::m_nNum << endl;
	cout << "a1.m_nNum = " << a1.m_nNum << endl;
	cout << "a1.GetNum2() = " << a1.GetNum2() << endl;
	cout << "a1.StaticGetNum3() = " << a1.StaticGetNum3() << endl;
	cout << endl;

	cout << "建立物件a2" << endl;
	A a2;
	cout << "A::m_nNum = " << A::m_nNum << endl;
	cout << "a2.m_nNum = " << a2.m_nNum << endl;
	cout << "a2.GetNum2() = " << a2.GetNum2() << endl;
	cout << "執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100" << endl;
	a2.SetNum3(100);
	cout << "a2.StaticGetNum3() = " << a2.StaticGetNum3() << endl;
	cout << endl;

	cout << "回到物件a1" << endl;
	//下面這行會有問題
	cout << "a1.StaticGetNum3() = " << a1.StaticGetNum3() << endl;
	cout << "a1.GetNum3() = " << a1.GetNum3() << endl;
	cout << endl;

	system("pause");
	return 0;
}

執行結果

建立物件a1
A::m_nNum = 0
a1.m_nNum = 0
a1.GetNum2() = 10
a1.StaticGetNum3() = 20

建立物件a2
A::m_nNum = 0
a2.m_nNum = 0
a2.GetNum2() = 10
執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100
a2.StaticGetNum3() = 100

回到物件a1
a1.StaticGetNum3() = 100
a1.GetNum3() = 20

值得注意的是 a1.StaticGetNum3() = 100,a1 的 m_nNum3 是 100 嗎?a1 的 m_nNum3 明明就是 20,而 a2 的 m_nNum3 才是100,那為什麼這裡會錯了呢?

主要的問題是來自於 m_pThis,我們為了讓靜態成員函式可以存取一般成員函式和成員變數,所以使用 m_pThis 指向自己來取得一般成員函式和成員變數,而且為了讓靜態成員函式可以使用 m_pThis 指標,還把 m_pThis 指標宣告成 static 的。

宣告成 static 就表示他是所有物件共用的,所以當產生 a1 物件時,m_pThis 是指向 a1 物件的,而產生 a2 物件時 m_pThis 就改指向 a2 物件了,所以就算透過 a1 物件取得 m_pThis,也都會取得 a2 物件了

想要用靜態成員函式 存取 一般成員變數,那又要怎麼解決這個 m_pThis 的問題呢?

就是把物件自己當成參數傳進靜態成員函式裡。我們先把 m_pThis 拿掉。

#include <iostream>
using namespace std;

class A
{
public:
	static int m_nNum;	//宣告static member variable
private:
	int m_nNum2;
	int m_nNum3;
public:
	A() { m_nNum2 = 10; m_nNum3 = 20;}
	static int GetNum2(A *a) { return a->m_nNum2; }
	static int StaticGetNum3(A* a) { return a->GetNum3(); }

	void SetNum3(int n) { m_nNum3 = n; }
	int GetNum3() { return m_nNum3; }
};

int A::m_nNum = 0;

int main()
{
	cout << "建立物件a1" << endl;
	A a1;
	cout << "A::m_nNum = " << A::m_nNum << endl;
	cout << "a1.m_nNum = " << a1.m_nNum << endl;
	cout << "a1.GetNum2(&a1) = " << a1.GetNum2(&a1) << endl;
	cout << "a1.StaticGetNum3(&a1) = " << a1.StaticGetNum3(&a1) << endl;
	cout << endl;

	cout << "建立物件a2" << endl;
	A a2;
	cout << "A::m_nNum = " << A::m_nNum << endl;
	cout << "a2.m_nNum = " << a2.m_nNum << endl;
	cout << "a2.GetNum2(&a2) = " << a2.GetNum2(&a2) << endl;
	cout << "執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100" << endl;
	a2.SetNum3(100);
	cout << "a2.StaticGetNum3(&a2) = " << a2.StaticGetNum3(&a2) << endl;
	cout << "a2.GetNum3() = " << a2.GetNum3() << endl;
	cout << endl;

	cout << "回到物件a1" << endl;
	cout << "a1.StaticGetNum3(&a1) = " << a1.StaticGetNum3(&a1) << endl;
	cout << "a1.GetNum3() = " << a1.GetNum3() << endl;
	cout << endl;

	system("pause");
	return 0;
}

執行結果

建立物件a1
A::m_nNum = 0
a1.m_nNum = 0
a1.GetNum2(&a1) = 10
a1.StaticGetNum3(&a1) = 20

建立物件a2
A::m_nNum = 0
a2.m_nNum = 0
a2.GetNum2(&a2) = 10
執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100
a2.StaticGetNum3(&a2) = 100
a2.GetNum3() = 100

回到物件a1
a1.StaticGetNum3(&a1) = 20
a1.GetNum3() = 20

恩,解決了。其實 m_pThis 也不是不能用,只不過要注意 static 的特性就是了。

咦~這樣的話,前面是透過 a1 還是 a2 去呼叫還有差別嗎?當然沒有囉,所以呼叫時把前面的物件拿掉也可以。

#include <iostream>
using namespace std;

class A
{
public:
	static int m_nNum;	//宣告static member variable
private:
	int m_nNum2;
	int m_nNum3;
public:
	A() { m_nNum2 = 10; m_nNum3 = 20; }
	static int GetNum2(A* a) { return a->m_nNum2; }
	static int StaticGetNum3(A* a) { return a->GetNum3(); }

	void SetNum3(int n) { m_nNum3 = n; }
	int GetNum3() { return m_nNum3; }
};

int A::m_nNum = 0;

int main()
{
	cout << "建立物件a1" << endl;
	A a1;
	cout << "A::GetNum2(&a1) = " << A::GetNum2(&a1) << endl;
	cout << "A::StaticGetNum3(&a1) = " << A::StaticGetNum3(&a1) << endl;
	cout << endl;

	cout << "建立物件a2" << endl;
	A a2;
	cout << "A::GetNum2(&a2) = " << A::GetNum2(&a2) << endl;
	cout << "執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100" << endl;
	a2.SetNum3(100);
	cout << "A::StaticGetNum3(&a2) = " << A::StaticGetNum3(&a2) << endl;
	cout << endl;

	system("pause");
	return 0;
}
建立物件a1
A::GetNum2(&a1) = 10
A::StaticGetNum3(&a1) = 20

建立物件a2
A::GetNum2(&a2) = 10
執行 a2.SetNum3(100) 把 a2 的 m_nNum3 從 20 設成 100
A::StaticGetNum3(&a2) = 100

還要把物件自己傳進去,那跟一般的全域函式有什麼不一樣?

不一樣喔,來看看下面的例子

#include <iostream>
using namespace std;

class A
{
public:
	static int m_nNum;
private:
	int m_nNum2;
public:
	A() { m_nNum2 = 10;}
	static int GetNum2(A* a) { return a->m_nNum2; }
};

int A::m_nNum = 0;

int GlobalGetNum2(A* a)
{
	return a->m_nNum2;//不能存取
}

int main()
{
	cout << "建立物件a1" << endl;
	A a1;

	//下面這兩行有什麼不一樣?
	cout << "A::GetNum2(&a1) = " << A::GetNum2(&a1) << endl;
	cout << "GlobalGetNum2(&a1) = " << GlobalGetNum2(&a1) << endl;//不能存取
	cout << endl;

	system("pause");
	return 0;
}

一般的全域函式 GlobalGetNum2() 就不能存取 private 的 m_nNum2 了啊。

如果 m_nNum2 是 public 的,那靜態成員函式 GetNum2() 和 一般的全域函式 GlobalGetNum2() 確實就會沒什麼差別。

※再次聲明:「用靜態成員函式存取一般成員變數」是研究與實驗性質的嘗試,是為了在研究過程中更了解「 靜態宣告 」的特性,實務上是非常不建議這樣使用的。

非常不建議的原因是,靜態成員函式本來就是為了存取靜態成員變數存在,而想存取一般成員變數,本來就有一般成員函式可以用了。所以「用靜態成員函式來存取一般成員變數」,不只繞了路,還會造成對「靜態」定義的混淆,沒有任何好處喔。

回上一篇:C++ 的 static -靜態變數與函式詳細說明
https://husking-studio.com/cpp-static/

在〈C++ 的 static -靜態變數與函式的小實驗〉中有 2 則留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *