網站首頁 編程語言 正文
C++static詳解,類中static用法
static特點:用來控制存儲方式和可見性
① 存儲空間:靜態存儲區(控制變量的存儲方式)
靜態變量存儲在靜態存儲區(存儲在靜態存儲區的變量,如果不顯式地對其進行初始化,系統會將其初始化為0),在程序執行期間,對應的存儲空間不會釋放,一直到程序結束才會釋放。
static控制變量的存儲方式體現在局部變量上。局部變量存儲在動態存儲區,在局部變量定義前加上static,該局部變量就變成了局部靜態變量,局部靜態變量存儲在靜態存儲區,即使函數調用結束,它占用的存儲空間也不會釋放,但其他函數不能引用該局部靜態變量。當下一次調用該函數時,不會再次對該局部靜態變量進行初始化,它的值是上一次函數調用結束時的值。
對全局變量而言,存儲方式沒有什么改變,因為全局變量和全局靜態變量都存儲在靜態存儲區。
② 作用域:(控制變量、函數的可見性)
static控制變量的可見性體現在全局靜態變量和靜態函數上。
全局變量默認具有外部鏈接性,作用域是整個工程。使用static關鍵字可以限制全局變量的作用域,全局靜態變量的作用域僅限本文件,它對在其他文件不可見,也就是說不能在其他文件中引用該全局靜態變量,但其他文件中可以定義和它名字相同的變量,不會發生沖突。
在函數的返回類型前加上static關鍵字,函數即被定義為靜態函數。靜態函數與普通函數的不同在于,它只能在聲明它的文件當中可見,不能被其它文件使用,其它文件中可以定義相同名字的函數,不會發生沖突。
局部靜態變量的作用域與局部變量的作用域相同,其作用域都是從定義開始到函數或程序塊結束為止。
類中的static關鍵字
在類中聲明static變量或者函數時,初始化時使用作用域運算符(::)來標明它所屬類,靜態成員是類的成員(所有對象中共享的成員),而不是某一個對象的成員。
① 靜態數據成員
在類內數據成員的聲明前加上關鍵字static,該數據成員就是類內的靜態數據成員。
靜態數據成員和普通數據成員一樣遵從public,protected,private訪問規則。
對于非靜態數據成員,每個對象都有自己的拷貝。而靜態數據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態數據成員在程序中也只有一份拷貝,由該類的所有對象共享訪問。也就是說,靜態數據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態數據成員只會被分配一次內存,供所有對象共用。所以,靜態數據成員的值對每個對象都是一樣的,它的值可以更新。
因為靜態數據成員在全局數據區分配內存,屬于本類的所有對象共享,所以,它不屬于特定的對象,在沒有產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它。
同全局變量相比,使用靜態數據成員有兩個優勢:
靜態數據成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字沖突的可能性;
可以實現信息隱藏。靜態數據成員可以是private成員,而全局變量不能;
② 靜態成員函數
與靜態數據成員一樣,我們也可以創建一個靜態成員函數,它為類的全部服務而不是為某一個類的具體對象服務。靜態成員函數與靜態數據成員一樣,都是類的內部實現,屬于類定義的一部分。普通的成員函數一般都隱含了一個this指針,this指針指向類的對象本身,因為普通成員函數總是具體的屬于某個類的具體對象的。通常情況下,this是缺省的,如函數fun()實際上是this->fun()。但是與普通函數相比,靜態成員函數由于不與任何的對象相聯系,因此它不具有this指針。
非靜態成員函數可以任意地訪問靜態成員函數和靜態數據成員;
靜態成員函數不能訪問非靜態成員函數和非靜態數據成員;靜態成員之間可以相互訪問,包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數;
什么時候用static?
需要一個數據對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見。
為什么要引入static?
函數內部定義的變量,在程序執行到它的定義處時,編譯器為它在棧上分配空間,大家知道,函數在棧上分配的空間在此函數執行結束時會釋放掉,這樣就產生了一個問題:如果想將函數中此變量的值保存至下一次調用時,如何實現?最容易想到的方法是定義一個全局的變量,但定義為一個全局變量有許多缺點,最明顯的缺點是破壞了此變量的訪問范圍(使得在此函數中定義的變量,不僅僅受此函數控制)。
c++中static總結
經過static修飾的變量,存儲在內存的全局靜態區。且被static修飾的變量只能在本模塊的所有函數引用。
內存中的存儲區域如下:
- 堆區:是由程序員手動申請(new)與釋放(delete)的內存區域。從低地址向高地址申請;內存空間大、存儲地址不連續,一般是鏈式的;速度較慢。
- 棧區:由編譯器自動分配和釋放,主要存儲 函數的參數值、函數內部的變量的值、函數調用的空間。從高地址向低地址申請;容量有限;速度較快;存儲地址連續,會溢出。
- 代碼區:又叫文本段(.text),存放著程序的機器代碼,可執行指令就是存儲在這里的,這里的代碼是只讀的。
- 全局區(靜態區):全局變量和靜態變量是存儲在這里的。初始化的全局變量和靜態變量在一塊區域(.data),未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域(.bbs)。系統結束后由系統釋放。
- 常量區:常量字符串放在這里,程序結束后,由系統進行釋放。
1. 概念
static的用法主要體現在兩個方面:面向過程中的static和面向對象中的static。
面向過程的static主要包括靜態全局變量、靜態局部變量和靜態函數。
面向對象的static主要包括靜態成員變量、靜態成員函數。
2. 面向過程的static
2.1 靜態全局變量
全局變量前添加static關鍵字,則該變量稱為靜態全局變量。
#include <iostream>
#include <stdio.h>
?
static int a = 10;
?
void Func()
{
?? ?a++;
}
?
int main()
{
?? ?printf("a = %d\n", a);//輸出:10
?? ?Func();//輸出:10
?? ?printf("a = %d\n", a);//輸出:11
?
?? ?system("pause");
? ? return 0;
}
特點:
1)在全局數據中的變量如果沒有顯示的初始化會自動被程序初始化為0(這個特性非靜態全局變量也有),而在函數體內聲明的變量如果不顯示初始化則會使一個隨機值;
2)靜態全局變量在聲明它的整個文件中都是可見的,而在文件之外是不可見的;
3)靜態全局變量在全局數據區分配內存;
4)其他文件中可以定義同名int型變量a,不會沖突;
如果將static去掉,具有以下特點:
1)全局變量默認是有外部連接性的,其作用域是整個工程,在一個文件內定義的全局變量可以通過包含其所在頭文件或顯示調用 extern關鍵字修飾全局變量的變量名聲明來引用;
2)靜態全局變量是顯示調用static修飾的全局變量,其作用域只在聲明此變量的文件中,其他文件即使使用extern關鍵字修飾其聲明也不可使用;
2.2 靜態局部變量
局部變量前添加static關鍵字,則該變量稱為靜態局部變量。
#include <iostream>
#include <stdio.h>
?
void Func()
{
?? ?static int a = 5;
?? ?printf("a = %d\n", a);
?? ?a++;
}
?
int main()
{
?? ?for (int i = 0; i < 5; i++)
?? ?{
?? ??? ?Func(); ?//打印結果:5 6 7 8 9
?? ?}
?
?? ?system("pause");
? ? return 0;
}
通常,在一個函數作用域內定義一個變量,每次運行到該函數時,系統會給局部變量分配內存。當函數結束時,該變量的內存會被系統回收至棧內存當中。
特點:
1)內存存放在程序的全局數據區中;
2)靜態局部變量在程序執行到該對象聲明時,會被首次初始化。其后運行到該對象的聲明時,不會再次初始化,這也是為什么上面程序測試函數每次輸出的值都是遞增的原因(只會被初始化一次);
3)如果靜態局部變量沒有被顯式初始化,則其值會自動被系統初始化為0;
4)局部靜態變量不能被其作用域之外的其他模塊調用,其調用范圍僅限于聲明該變量的函數作用域當中;
2.3 靜態函數
函數返回類型前添加static關鍵字,則該變量稱為靜態函數。
#include <iostream>
#include <stdio.h>
?
static void Func()
{
?? ?printf("This is a static function\n");
}
?
int main()
{
?? ?Func();?
?? ?
?? ?system("pause");
? ? return 0;
}
特點:
1)作用域只在聲明它的文件當中,不能被其他文件引用,其他文件可以定義同名的全局函數;
2)其他文件想要調用本文件的靜態函數,需要顯示的調用extern關鍵字修飾其聲明;
3. 面向對象中的static?
3.1 靜態成員變量
#include <iostream>
#include <stdio.h>
using namespace std;
?
class Test
{
public:
?? ?Test(int a, int b, int c) :?
?? ??? ?m_a(a),
?? ??? ?m_b(b),
?? ??? ?m_c(c)
?? ?{
?? ??? ?m = a + b + c;
?? ?}
?
?? ?void Show()
?? ?{
?? ??? ?cout << "m = " << m << endl;
?? ?}
?
private:
?? ?int m_a, m_b, m_c;
?? ?static int m;
};
?
int Test::m = 0; //初始化靜態數據成員
?
int main()
{
?? ?Test ClassA(1, 1, 1);
?? ?ClassA.Show(); ?//輸出: 3
?? ?Test ClassB(3, 3, 3);
?? ?ClassB.Show(); //輸出: 9
?? ?ClassA.Show(); //輸出: 9
?
?? ?system("pause");
?? ?return 0;
}
特點:
1)靜態數據成員的服務對象并非是單個類實例化的對象,而是所有類實例化的對象(這點可以用于設計模式中的單例模式實現);
2)靜態數據成員必須顯式的初始化分配內存,在其包含類沒有任何實例化之前已經有內存分配;
3)靜態數據成員與其他成員一樣,遵從public,protected,private的訪問規則;
4)靜態數據成員內存存儲在全局數據區,只隨著進程的消亡而消亡;
優勢:
1)靜態數據成員不進入程序全局命名空間,不會與其他全局名稱的同名同類型變量沖突;
2)靜態數據成員可以實現C++的封裝特性,由于其遵守類的訪問權限規則,所以相比全局變量更加靈活;
3.2 靜態成員函數
類的成員函數返回類型之前添加static,此成員函數為靜態成員函數。
#include <iostream>
#include <stdio.h>
using namespace std;
?
class Test
{
public:
?? ?Test(int a, int b, int c) :?
?? ??? ?m_a(a),
?? ??? ?m_b(b),
?? ??? ?m_c(c)
?? ?{
?? ??? ?m = a + b + c;
?? ?}
?
?? ?static void Show()
?? ?{
?? ??? ?cout << "m = " << m << endl;
?? ?}
?
private:
?? ?int m_a, m_b, m_c;
?? ?static int m;
};
?
int Test::m = 0; //初始化靜態數據成員
?
int main()
{
?? ?Test ClassA(1, 1, 1);
?? ?ClassA.Show();
?? ?Test ClassB(3, 3, 3);
?? ?ClassB.Show();
?? ?ClassA.Show();
?
? ? Test::Show(); //輸出: 9
?
?? ?system("pause");?
?? ?return 0;
}
特點:
1)靜態成員函數比普通成員函數多了一種調用方式;
2)在沒有實例化的類對象的條件下可以調用類的靜態成員函數;
3)靜態成員函數中沒有隱含的this指針,所以靜態成員函數不可以操作類中的非靜態成員(由于第二條可知,類的非靜態成員是在類實例化后存在的,而類的成員函數可以在類沒有實例化的時候調用,故不能操作類的非靜態成員);
4. 小結
1)靜態數據成員都是靜態存儲的,所以必須在main函數之前顯示的對其進行初始化;
2)不能再頭文件中聲明靜態全局變量,原因是可能是產生了多個同名的靜態數據;
3)不能將靜態成員函數定義為虛函數;
4)靜態成員函數沒有this指針;
5)static縮短了子類對父類靜態成員訪問的時間,相對來說節省了內存空間;
6)如果不想在子類中操作父類的靜態成員,則可以在子類中定義一個同名的static成員。這樣既可覆蓋父類中的靜態成員,并且根據C++的多態性變量命名規則,這樣做是安全的;
7)靜態成員聲明在類中,操作在其外部,所以對其取地址操作就跟取普通成員的操作略有不同。靜態變量地址是指向其數據類型的指針,函數地址則是一個類型為nonmember的函數指針;?
原文鏈接:https://blog.csdn.net/weixin_43222324/article/details/106999558
相關推薦
- 2022-06-14 GO語言結構體面向對象操作示例_Golang
- 2022-07-29 C語言深入回顧講解結構體對齊_C 語言
- 2022-11-28 詳解Rust中的變量與常量_Rust語言
- 2022-05-24 Flutter滾動組件之SingleChildScrollView使用詳解_Android
- 2022-03-06 C#多種操作excel的方法比較_C#教程
- 2022-01-21 面試題:說一說es6新增方法
- 2022-04-09 SpringBoot添加Cors跨域配置,解決No Access-Control-Allow-Ori
- 2022-06-09 ASP.NET?Core使用EF創建模型(索引、備用鍵、繼承、支持字段)_實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支