国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++:基礎(chǔ)知識(shí)

這篇具有很好參考價(jià)值的文章主要介紹了C++:基礎(chǔ)知識(shí)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、內(nèi)存四區(qū)

在C++程序執(zhí)行時(shí),內(nèi)存可以被劃分為以下四個(gè)區(qū)域:

(1)代碼區(qū)(Code Segment:代碼區(qū)的聲明周期從程序加載到內(nèi)存開始,一直持續(xù)到程序結(jié)束。代碼區(qū)中存儲(chǔ)的是程序的機(jī)器指令代碼,這些指令在程序執(zhí)行過程中被逐條執(zhí)行。代碼區(qū)的內(nèi)容是只讀的,不會(huì)被修改。

(2)全局?jǐn)?shù)據(jù)區(qū)(Global Data):全局?jǐn)?shù)據(jù)區(qū)是用于存儲(chǔ)全局變量和靜態(tài)變量的區(qū)域。全局變量和靜態(tài)變量在程序運(yùn)行期間一直存在,它們的內(nèi)存分配是在程序啟動(dòng)時(shí)完成的。全局?jǐn)?shù)據(jù)區(qū)包括靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)。

  • 靜態(tài)存儲(chǔ)區(qū)(Static Storage):靜態(tài)變量和局部靜態(tài)變量在靜態(tài)存儲(chǔ)區(qū)分配內(nèi)存,它們的生命周期與程序的運(yùn)行周期相同,即從程序啟動(dòng)到程序結(jié)束。
  • 常量存儲(chǔ)區(qū)(Constant Storage):常量數(shù)據(jù)(如字符串常量)在常量存儲(chǔ)區(qū)分配內(nèi)存,它們的值在程序運(yùn)行期間保持不變。

(3)棧區(qū)(Stack Segment:棧區(qū)的聲明周期與函數(shù)的調(diào)用和返回相關(guān)。當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),它的局部變量、函數(shù)參數(shù)和一些與函數(shù)調(diào)用相關(guān)的上下文信息會(huì)被分配到棧區(qū)。當(dāng)函數(shù)執(zhí)行完畢并返回時(shí),棧上的這些數(shù)據(jù)會(huì)被釋放。棧區(qū)的大小是動(dòng)態(tài)變化的,隨著函數(shù)的嵌套調(diào)用和返回而動(dòng)態(tài)分配和釋放。

問題局部變量為什么放到棧里面?

答:局部變量的生命周期相對(duì)較短,當(dāng)調(diào)用一個(gè)函數(shù)時(shí),函數(shù)的局部變量被推入棧,當(dāng)函數(shù)返回時(shí),局部變量被彈出棧。

(4)堆區(qū)(Heap Segment:堆區(qū)的聲明周期由程序員手動(dòng)管理。程序在運(yùn)行時(shí)可以通過動(dòng)態(tài)內(nèi)存分配函數(shù)(如malloc或new)從堆區(qū)申請(qǐng)一塊內(nèi)存,并在不需要時(shí)手動(dòng)釋放(使用free或delete)。堆區(qū)的內(nèi)存分配和釋放不受函數(shù)調(diào)用的影響,可以在程序的任意階段進(jìn)行操作。

C++:基礎(chǔ)知識(shí)

2、struct和class的區(qū)別

struct class
相同點(diǎn)
  • 兩者都擁有成員函數(shù)、公有和私有部分
  • 任何可以使用class完成的工作,同樣可以使用struct完成
不同點(diǎn) struct默認(rèn)是public繼承 class默認(rèn)是private繼承

3、final和override關(guān)鍵字

當(dāng)不希望某個(gè)類被繼承,或不希望某個(gè)虛函數(shù)被重寫,可以在類名和虛函數(shù)后添加final關(guān)鍵字,添加final關(guān)鍵字后被繼承或重寫,編譯器會(huì)報(bào)錯(cuò)。例子如下:

class Base
{
    virtual void foo();
};
 
class A : public Base
{
    void foo() final; // foo 被override并且是最后一個(gè)override,在其子類中不可以重寫
};

class B final : A // 指明B是不可以被繼承的
{
    void foo() override; // Error: 在A中已經(jīng)被final了
};
 
class C : B // Error: B is final
{
};

當(dāng)在父類中使用了虛函數(shù)時(shí)候,你可能需要在某個(gè)子類中對(duì)這個(gè)虛函數(shù)進(jìn)行重寫(override),以下方法都可以:

class A
{
    virtual void foo();
}
class B : public A
{
    void foo(); //OK
    virtual void foo(); // OK
    void foo() override; //OK
}

4、淺拷貝和深拷貝

(1)淺拷貝

????????淺拷貝只是拷貝一個(gè)指針,并沒有新開辟一個(gè)地址,拷貝的指針和原來的指針指向同一塊地址,如果原來的指針?biāo)赶虻馁Y源釋放了,那么再釋放淺拷貝的指針的資源就會(huì)出現(xiàn)錯(cuò)誤。

(2)深拷貝

深拷貝不僅拷貝值,還開辟出一塊新的空間用來存放新的值,即使原先的對(duì)象被析構(gòu)掉,釋放內(nèi)存了也不會(huì)影響到深拷貝得到的值。在自己實(shí)現(xiàn)拷貝賦值的時(shí)候,如果有指針變量的話是需要自己實(shí)現(xiàn)深拷貝的。

示例如下:

#include <iostream>  
#include <string.h>
using namespace std;
 
class Student
{
private:
	int num;
	char *name;
public:
	Student(){
        name = new char(20);
		cout << "Student" << endl;
    };
	~Student(){
        cout << "~Student " << &name << endl;
        delete name;
        name = NULL;
    };
	Student(const Student &s){//拷貝構(gòu)造函數(shù)
        //淺拷貝,當(dāng)對(duì)象的name和傳入對(duì)象的name指向相同的地址
        name = s.name;
        //深拷貝
        //name = new char(20);
        //memcpy(name, s.name, strlen(s.name));
        cout << "copy Student" << endl;
    };
};
 
int main()
{
	{// 花括號(hào)讓s1和s2變成局部對(duì)象,方便測(cè)試
		Student s1;
		Student s2(s1);// 復(fù)制對(duì)象
	}
	system("pause");
	return 0;
}
//淺拷貝執(zhí)行結(jié)果:
//Student
//copy Student
//~Student 0x7fffed0c3ec0
//~Student 0x7fffed0c3ed0
//*** Error in `/tmp/815453382/a.out': double free or corruption (fasttop): 0x0000000001c82c20 ***

//深拷貝執(zhí)行結(jié)果:
//Student
//copy Student
//~Student 0x7fffebca9fb0
//~Student 0x7fffebca9fc0

5、內(nèi)聯(lián)函數(shù)和宏定義

內(nèi)聯(lián)函數(shù):是一種特殊的函數(shù),編譯器會(huì)嘗試將其內(nèi)聯(lián)展開,而不是通過函數(shù)調(diào)用的方式執(zhí)行。內(nèi)聯(lián)函數(shù)通常用于執(zhí)行簡(jiǎn)單的操作或者頻繁調(diào)用的函數(shù),以減少函數(shù)調(diào)用的開銷和提高性能。

  1. 在使用時(shí),宏只做簡(jiǎn)單字符串替換(編譯前)。而內(nèi)聯(lián)函數(shù)可以進(jìn)行參數(shù)類型檢查(編譯時(shí)),且具有返回值。
  2. 內(nèi)聯(lián)函數(shù)在編譯時(shí)直接將函數(shù)代碼嵌入到目標(biāo)代碼中,省去函數(shù)調(diào)用的開銷來提高執(zhí)行效率,并且進(jìn)行參數(shù)類型檢查,具有返回值,可以實(shí)現(xiàn)重載。
  3. 宏定義時(shí)要注意書寫(參數(shù)要括起來)否則容易出現(xiàn)歧義,內(nèi)聯(lián)函數(shù)不會(huì)產(chǎn)生歧義。
  4. 內(nèi)聯(lián)函數(shù)有類型檢測(cè)、語法判斷等功能,而宏沒有。

內(nèi)聯(lián)函數(shù)適用場(chǎng)景:

  • 使用宏定義的地方都可以使用 inline 函數(shù)。
  • 作為類成員接口函數(shù)來讀寫類的私有成員或者保護(hù)成員,會(huì)提高效率。

內(nèi)聯(lián)函數(shù)的示例:

#include <iostream>

// 內(nèi)聯(lián)函數(shù)示例
inline int addNumbers(int a, int b) {
    return a + b;
}

int main() {
    int result = addNumbers(5, 3);  // 內(nèi)聯(lián)函數(shù)調(diào)用
    std::cout << "Result: " << result << std::endl;

    return 0;
}

6、 new和delete

new的實(shí)現(xiàn)原理

  1. 當(dāng)使用new運(yùn)算符分配內(nèi)存時(shí),編譯器會(huì)首先計(jì)算所需的內(nèi)存大小,包括對(duì)象本身的大小和可能的額外內(nèi)存(如虛函數(shù)表等)。
  2. 編譯器會(huì)生成相應(yīng)的代碼,調(diào)用運(yùn)行時(shí)庫中的分配函數(shù)(如operator new)來分配所需大小的內(nèi)存塊。
  3. 運(yùn)行時(shí)庫會(huì)使用底層的內(nèi)存分配機(jī)制(如操作系統(tǒng)提供的malloc()函數(shù))來獲取一塊連續(xù)的內(nèi)存空間。
  4. 運(yùn)行時(shí)庫會(huì)將分配到的內(nèi)存進(jìn)行適當(dāng)?shù)膶?duì)齊,并返回一個(gè)指向該內(nèi)存塊的指針。

delete 的實(shí)現(xiàn)原理:

  1. 當(dāng)使用 delete 運(yùn)算符釋放內(nèi)存時(shí),編譯器會(huì)生成相應(yīng)的代碼,調(diào)用運(yùn)行時(shí)庫中的釋放函數(shù)(如 operator delete)。
  2. 運(yùn)行時(shí)庫會(huì)接收到要釋放的內(nèi)存指針,并執(zhí)行必要的清理操作(如調(diào)用對(duì)象的析構(gòu)函數(shù))。
  3. 運(yùn)行時(shí)庫會(huì)使用底層的內(nèi)存釋放機(jī)制(如操作系統(tǒng)提供的 free() 函數(shù))來釋放內(nèi)存空間。

7、malloc與free的實(shí)現(xiàn)原理

  1. 在標(biāo)準(zhǔn)C庫中,提供了malloc/free函數(shù)分配釋放內(nèi)存,這兩個(gè)函數(shù)底層是由brk、mmap、munmap這些系統(tǒng)調(diào)用實(shí)現(xiàn)的。
  2. brk是將數(shù)據(jù)段(.data)的最高地址指針_edata往高地址推,mmap是在進(jìn)程的虛擬地址空間中(堆和棧中間,稱為文件映射區(qū)域的地方)找一塊空閑的虛擬內(nèi)存。這兩種方式分配的都是虛擬內(nèi)存,沒有分配物理內(nèi)存。在第一次訪問已分配的虛擬地址空間的時(shí)候,發(fā)生缺頁中斷,操作系統(tǒng)負(fù)責(zé)分配物理內(nèi)存,然后建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。
  3. malloc小于128k的內(nèi)存,使用brk分配內(nèi)存,將_edata往高地址推;malloc大于128k的內(nèi)存,使用mmap分配內(nèi)存,在堆和棧之間找一塊空閑內(nèi)存分配;brk分配的內(nèi)存需要等到高地址內(nèi)存釋放以后才能釋放,而mmap分配的內(nèi)存可以單獨(dú)釋放。當(dāng)最高地址空間的空閑內(nèi)存超過128K(可由M_TRIM_THRESHOLD選項(xiàng)調(diào)節(jié))時(shí),執(zhí)行內(nèi)存緊縮操作(trim)。在上一個(gè)步驟free的時(shí)候,發(fā)現(xiàn)最高地址空閑內(nèi)存超過128K,于是內(nèi)存緊縮。
  4. malloc是從堆里面申請(qǐng)內(nèi)存,也就是說函數(shù)返回的指針是指向堆里面的一塊內(nèi)存。操作系統(tǒng)中有一個(gè)記錄空閑內(nèi)存地址的鏈表。當(dāng)操作系統(tǒng)收到程序的申請(qǐng)時(shí),就會(huì)遍歷該鏈表,然后就尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后就將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序。

8、類成員初始化方式?構(gòu)造函數(shù)的執(zhí)行順序 ?為什么用成員初始化列表會(huì)快一些?

初始化方式有兩種:

(1)、賦值初始化,通過在函數(shù)體內(nèi)進(jìn)行賦值初始化;

class MyClass {
private:
    int number;
    std::string name;

public:
    MyClass() {
        number = 0;
        name = "Default";
    }
    
    void printData() {
        std::cout << "Number: " << number << std::endl;
        std::cout << "Name: " << name << std::endl;
    }
};

(2)、列表初始化,在冒號(hào)后使用初始化列表進(jìn)行初始化。

class MyClass {
private:
    int number;
    std::string name;

public:
    MyClass(int num, const std::string& n) : number(num), name(n) {
        // 構(gòu)造函數(shù)體
    }
    
    void printData() {
        std::cout << "Number: " << number << std::endl;
        std::cout << "Name: " << name << std::endl;
    }
};

這兩種方式的主要區(qū)別在于:

函數(shù)體中初始化,是在所有的數(shù)據(jù)成員被分配內(nèi)存空間后才進(jìn)行的。

列表初始化是給數(shù)據(jù)成員分配內(nèi)存空間時(shí)就進(jìn)行初始化,就是說分配一個(gè)數(shù)據(jù)成員只要冒號(hào)后有此數(shù)據(jù)成員的賦值表達(dá)式(此表達(dá)式必須是括號(hào)賦值表達(dá)式),那么分配了內(nèi)存空間后在進(jìn)入函數(shù)體之前給數(shù)據(jù)成員賦值,就是說初始化這個(gè)數(shù)據(jù)成員此時(shí)函數(shù)體還未執(zhí)行。

一個(gè)派生類構(gòu)造函數(shù)的執(zhí)行順序如下:

① 虛擬基類的構(gòu)造函數(shù)(多個(gè)虛擬基類則按照繼承的順序執(zhí)行構(gòu)造函數(shù))。

② 基類的構(gòu)造函數(shù)(多個(gè)普通基類也按照繼承的順序執(zhí)行構(gòu)造函數(shù))。

③ 類類型的成員對(duì)象的構(gòu)造函數(shù)(按照成員對(duì)象在類中的定義順序)

④ 派生類自己的構(gòu)造函數(shù)。

為什么用成員初始化列表會(huì)快一些?

????????賦值初始化是在構(gòu)造函數(shù)當(dāng)中做賦值的操作,而列表初始化是做純粹的初始化操作。我們都知道,C++的賦值操作是會(huì)產(chǎn)生臨時(shí)對(duì)象的。臨時(shí)對(duì)象的出現(xiàn)會(huì)降低程序的效率。

????????通過構(gòu)造函數(shù)初始化列表,編譯器可以生成更高效的代碼,避免了臨時(shí)對(duì)象的創(chuàng)建和額外的賦值操作。這種直接初始化的方式可以提高代碼的性能,并且在某些情況下,還可以優(yōu)化構(gòu)造函數(shù)的調(diào)用。

9、STL

*max_element用法

最大值*max_element,最小值*min_element,求和accumulate。

*min_element 和 *max_element頭文件是algorithm,返回值是一個(gè)迭代器。

accumulate 頭文件是numeric,第三個(gè)參數(shù)是初始值,返回值是一個(gè)數(shù)。

#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric> 


int a[] = {1, 2, 3, 4, 5};
vector<int> v({1, 2, 3, 4, 5});

// 普通數(shù)組
int minValue = *min_element(a, a + 5); 
int maxValue = *max_element(a, a + 5); int sumValue = accumulate(a, a + 5, 0);
 
// Vector數(shù)組  
int minValue2 = *min_element(v.begin(), v.end());
int maxValue2 = *max_element(v.begin(), v.end());
int sumValue2 = accumulate(v.begin(), v.end(), 0);

10、c++類的默認(rèn)六個(gè)成員函數(shù)詳解

  1. 構(gòu)造函數(shù)
  2. 析構(gòu)函數(shù)
  3. 拷貝構(gòu)造
  4. 賦值運(yùn)算符重載
  5. 取地址運(yùn)算符重載
  6. const修飾的取地址運(yùn)算符重載
1、構(gòu)造函數(shù)

構(gòu)造函數(shù)就是在創(chuàng)建類對(duì)象的時(shí)候,由編譯器自動(dòng)調(diào)用,為對(duì)象進(jìn)行初始化的一個(gè)特殊成員函數(shù)。它的名稱和類名相同,并且在對(duì)象的聲明周期內(nèi)只調(diào)用一次。

構(gòu)造函數(shù)的特性:

1)函數(shù)名與類名相同
2)無返回值
3)對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)
4)構(gòu)造函數(shù)可以重載
5)如果用戶沒有自己定義構(gòu)造函數(shù),那么編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造,若用戶定義了,則編譯器不再自動(dòng)生成。
6)無參構(gòu)造和全缺省的構(gòu)造統(tǒng)稱為“默認(rèn)構(gòu)造函數(shù)”,并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。(無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)成員函數(shù)。若無參的和全缺省的同時(shí)存在,要構(gòu)造一個(gè)無參對(duì)象時(shí)會(huì)出錯(cuò))
無參構(gòu)造和全缺省構(gòu)造構(gòu)成函數(shù)重載,但是不可以同時(shí)存在。

7)編譯器生成無參默認(rèn)構(gòu)造,什么也沒有實(shí)現(xiàn),有什么用?
解:不僅是編譯器生成的默認(rèn)構(gòu)造,還有自己寫的無參默認(rèn)構(gòu)造,構(gòu)造后打印其成員變量的值都為亂碼。但是,因?yàn)镃++把類型分成內(nèi)置類型(基本類型)和自定義類型。內(nèi)置類型就是語法已經(jīng)定義好的類型:如int/char…,自定義類型就是我們使用class/struct/union自己定義的類型,**編譯器會(huì)自動(dòng)調(diào)用自定義類型成員的默認(rèn)構(gòu)造函數(shù)。**而不對(duì)內(nèi)置類型做任何處理。

2、析構(gòu)函數(shù)

析構(gòu)函數(shù)是一個(gè)對(duì)象在銷毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象銷毀前,類的一些資源清理工作。(不是完成對(duì)象的銷毀,局部對(duì)象銷毀工作是由編譯器完成的)

析構(gòu)函數(shù)的特性:

1)析構(gòu)函數(shù),名是在類名前加上字符~。
2)無參數(shù),無返回值
3)一個(gè)類只有一個(gè)析構(gòu)函數(shù),如果用戶自己沒有定義,則系統(tǒng)會(huì)自動(dòng)生成一個(gè)。
4)對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。

5)編譯器自動(dòng)生成的析構(gòu)函數(shù),會(huì)對(duì)自定義類型成員調(diào)用它的析構(gòu)函數(shù)

3、拷貝構(gòu)造函數(shù)?

只有單個(gè)形參,并且該形參是對(duì)本類類型對(duì)象的引用(一般用const修飾),在用已存在類類型對(duì)象創(chuàng)建新對(duì)象時(shí),編譯器會(huì)自動(dòng)調(diào)用。

拷貝構(gòu)造函數(shù)的特性:

1)拷貝構(gòu)造只是構(gòu)造函數(shù)的一個(gè)重載
2)拷貝構(gòu)造的參數(shù)只有一個(gè),且必須引用傳參,使用傳值方式會(huì)引發(fā)無窮遞歸調(diào)用。

3)若用戶沒有定義拷貝構(gòu)造,編譯器會(huì)自動(dòng)生成默認(rèn)拷貝構(gòu)造,但是只是按對(duì)象字節(jié)序進(jìn)行拷貝,是淺拷貝。

備注:(淺拷貝在拷貝指針的時(shí)候沒有重新開辟一塊空間將指針?biāo)竷?nèi)存單元的內(nèi)容拷貝到新空間里,再讓拷貝的指針指向它。所以淺拷貝只做了表面功夫,**拷貝的指針仍然和之前的指針指向同一片空間,**所以一旦任何一個(gè)指針銷毀,釋放空間,那么另一個(gè)的指針也會(huì)出錯(cuò)。)

4、賦值運(yùn)算符重載
運(yùn)算符的重載

運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類型函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。
函數(shù)名為:關(guān)鍵字operator后面接需要重載的符號(hào)
函數(shù)原型:返回值類型 operator操作符(參數(shù)列表)

賦值運(yùn)算符的重載

a)一個(gè)類如果沒有顯式定義賦值運(yùn)算符的重載,那么編譯器會(huì)自動(dòng)生成一個(gè),完成對(duì)象按字節(jié)序的值拷貝(淺拷貝)
b)如果類里面有指針,若同樣賦值運(yùn)算符利用淺拷貝進(jìn)行,那么程序會(huì)崩潰。

5、const成員

1、const 修飾的類成員函數(shù)

const修飾的類成員函數(shù)稱為const成員函數(shù),實(shí)際修飾的是該成員函數(shù)隱含的this指針,表明在該成員函數(shù)中不能對(duì)類的任何成員進(jìn)行修改。

2、const修飾的變量

1)const修飾常量,表示其值無法修改
const int i=6; int const i=6;
2)const指向常量的指針
a)
int a=1; int b=2; const int *p1=&a; p1=&b;//可以修改指針的指向 //*p1=3不可以通過指針修改指針指向內(nèi)存單元的值
指針p1不可修改它指向的內(nèi)存單元的值,但是p1可修改其指向的內(nèi)存單元。
b)
int a=1; int *const p1=&a;//初始化后不能指向其他內(nèi)存單元,但是可以通過指針改變其指向內(nèi)存單元的值
左定值,右定向
(*號(hào)在const的左邊,則指針?biāo)傅膬?nèi)存單元的值不能通過指針改變,*號(hào)在const的右邊,表示指針?biāo)赶虻膬?nèi)存單元不可以改變。)
3)指向常量的指針常量
int a=1; int const * const p1 =&a; //指針指向的值和指向的內(nèi)存單元都不可以改變

備注:

  1. const對(duì)象可以調(diào)用非const成員函數(shù)嗎?
    解:不可以
  2. 非const對(duì)象可以調(diào)用const成員函數(shù)嗎?
    解:可以
  3. const成員函數(shù)內(nèi)可以調(diào)用其它的非const成員函數(shù)嗎?
    解:不可以
  4. 非const成員函數(shù)內(nèi)可以調(diào)用其它的const成員函數(shù)嗎?
    解:可以
6、取地址運(yùn)算符重載及const取地址運(yùn)算符重載

一般情況下不用重新定義,編譯器會(huì)默認(rèn)生成。只有特殊情況下(想讓別人獲取到指定內(nèi)容時(shí))會(huì)重新定義。

11、單例模式

????????在一個(gè)項(xiàng)目中,全局范圍內(nèi),某個(gè)類的實(shí)例有且僅有一個(gè),通過這個(gè)唯一實(shí)例向其他模塊提供數(shù)據(jù)的全局訪問,這種模式就叫單例模式。單例模式的典型應(yīng)用就是任務(wù)隊(duì)列。

防護(hù)措施

????????如果使用單例模式,首先要保證這個(gè)類的實(shí)例有且僅有一個(gè),也就是說這個(gè)對(duì)象是獨(dú)生子女,如果我們實(shí)施計(jì)劃生育只生一個(gè)孩子,不需要也不能給再他增加兄弟姐妹。因此,就必須采取一系列的防護(hù)措施。對(duì)于類來說以上描述同樣適用。涉及一個(gè)類多對(duì)象操作的函數(shù)有以下幾個(gè):

構(gòu)造函數(shù):創(chuàng)建一個(gè)新的對(duì)象
拷貝構(gòu)造函數(shù):根據(jù)已有對(duì)象拷貝出一個(gè)新的對(duì)象
拷貝賦值操作符重載函數(shù):兩個(gè)對(duì)象之間的賦值

為了把一個(gè)類可以實(shí)例化多個(gè)對(duì)象的路堵死,可以做如下處理:

(1)、構(gòu)造函數(shù)私有化,在類內(nèi)部只調(diào)用一次,這個(gè)是可控的。

  • 由于使用者在類外部不能使用構(gòu)造函數(shù),所以在類內(nèi)部創(chuàng)建的這個(gè)唯一的對(duì)象必須是靜態(tài)的,這樣就可以通過類名來訪問了,為了不破壞類的封裝,我們都會(huì)把這個(gè)靜態(tài)對(duì)象的訪問權(quán)限設(shè)置為私有的。
  • 在類中只有它的靜態(tài)成員函數(shù)才能訪問其靜態(tài)成員變量,所以可以給這個(gè)單例類提供一個(gè)靜態(tài)函數(shù)用于得到這個(gè)靜態(tài)的單例對(duì)象。

(2)、拷貝構(gòu)造函數(shù)私有化或者禁用(使用 = delete)

(3)、拷貝賦值操作符重載函數(shù)私有化或者禁用(從單例的語義上講這個(gè)函數(shù)已經(jīng)毫無意義,所以在類中不再提供這樣一個(gè)函數(shù),故將它也一并處理一下。)

由于單例模式就是給類創(chuàng)建一個(gè)唯一的實(shí)例對(duì)象,所以它的 UML 類圖是很簡(jiǎn)單的:

C++:基礎(chǔ)知識(shí)

因此,定義一個(gè)單例模式的類的示例代碼如下:?

// 定義一個(gè)單例模式的類
class Singleton
{
public:
? ? // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
? ? Singleton(const Singleton& obj) = delete;
? ? Singleton& operator=(const Singleton& obj) = delete;
? ? static Singleton* getInstance();
private:
? ? Singleton() = default;
? ? static Singleton* m_obj;
};

在實(shí)現(xiàn)一個(gè)單例模式的類的時(shí)候,有兩種處理模式:

  • 餓漢模式
  • 懶漢模式
餓漢模式

????????餓漢模式就是在類加載的時(shí)候立刻進(jìn)行實(shí)例化,這樣就得到了一個(gè)唯一的可用對(duì)象。關(guān)于這個(gè)餓漢模式的類的定義如下:

// 餓漢模式
class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
        return m_taskQ;
    }
private:
    TaskQueue() = default;
    static TaskQueue* m_taskQ;
};
// 靜態(tài)成員初始化放到類外部處理
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;

int main()
{
    TaskQueue* obj = TaskQueue::getInstance();
}

定義這個(gè)單例類的時(shí)候,就把這個(gè)靜態(tài)的單例對(duì)象創(chuàng)建出來了。當(dāng)使用者通過 getInstance() 獲取這個(gè)單例對(duì)象的時(shí)候,它已經(jīng)被準(zhǔn)備好了。
注意事項(xiàng):類的靜態(tài)成員變量在使用之前必須在類的外部進(jìn)行初始化才能使用。

懶漢模式

懶漢模式是在類加載的時(shí)候不去創(chuàng)建這個(gè)唯一的實(shí)例,而是在需要使用的時(shí)候再進(jìn)行實(shí)例化。

(1)懶漢模式類的定義
// 懶漢模式
class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
        if(m_taskQ == nullptr)
        {
            m_taskQ = new TaskQueue;
        }
        return m_taskQ;
    }
private:
    TaskQueue() = default;
    static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;

????????在調(diào)用 getInstance() 函數(shù)獲取單例對(duì)象的時(shí)候,如果在單線程情況下是沒有什么問題的,如果是多個(gè)線程,調(diào)用這個(gè)函數(shù)去訪問單例對(duì)象就有問題了。假設(shè)有三個(gè)線程同時(shí)執(zhí)行了getInstance() 函數(shù),在這個(gè)函數(shù)內(nèi)部每個(gè)線程都會(huì) new 出一個(gè)實(shí)例對(duì)象。此時(shí),這個(gè)任務(wù)隊(duì)列類的實(shí)例對(duì)象不是一個(gè)而是 3 個(gè),很顯然這與單例模式的定義是相悖的。

(2) 線程安全問題
雙重檢查鎖定

對(duì)于餓漢模式是沒有線程安全問題的,在這種模式下訪問單例對(duì)象的時(shí)候,這個(gè)對(duì)象已經(jīng)被創(chuàng)建出來了。要解決懶漢模式的線程安全問題,最常用的解決方案就是使用互斥鎖??梢詫?chuàng)建單例對(duì)象的代碼使用互斥鎖鎖住,處理代碼如下:

class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
        m_mutex.lock();
10      if (m_taskQ == nullptr)
11      {
12            m_taskQ = new TaskQueue;
13      }
        m_mutex.unlock();
        return m_taskQ;
    }
private:
    TaskQueue() = default;
    static TaskQueue* m_taskQ;
    static mutex m_mutex;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;
mutex TaskQueue::m_mutex;

在上面代碼的 10~13 行這個(gè)代碼塊被互斥鎖鎖住了,也就意味著不論有多少個(gè)線程,同時(shí)執(zhí)行這個(gè)代碼塊的線程只能是一個(gè)(相當(dāng)于是嚴(yán)重限行了,在重負(fù)載情況下,可能導(dǎo)致響應(yīng)緩慢)。我們可以將代碼再優(yōu)化一下:

class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
9       if (m_taskQ == nullptr)
        {
            m_mutex.lock();
            if (m_taskQ == nullptr)
            {
                m_taskQ = new TaskQueue;
            }
            m_mutex.unlock();
        }
        return m_taskQ;
    }
private:
    TaskQueue() = default;
    static TaskQueue* m_taskQ;
    static mutex m_mutex;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;
mutex TaskQueue::m_mutex;

????????改進(jìn)的思路就是在加鎖、解鎖的代碼塊外層有添加了一個(gè) if判斷(第 9 行),這樣當(dāng)任務(wù)隊(duì)列的實(shí)例被創(chuàng)建出來之后,訪問這個(gè)對(duì)象的線程就不會(huì)再執(zhí)行加鎖和解鎖操作了(只要有了單例類的實(shí)例對(duì)象,限行就解除了),對(duì)于第一次創(chuàng)建單例對(duì)象的時(shí)候線程之間還是具有競(jìng)爭(zhēng)關(guān)系,被互斥鎖阻塞。上面這種通過兩個(gè)嵌套的 if 來判斷單例對(duì)象是否為空的操作就叫做雙重檢查鎖定

雙重檢查鎖定的問題

假設(shè)有兩個(gè)線程 A、B,當(dāng)線程 A 執(zhí)行到第 8 行時(shí)在線程 A 中 TaskQueue 實(shí)例對(duì)象 被創(chuàng)建,并賦值給 m_taskQ。

static TaskQueue* getInstance()
{
    if (m_taskQ == nullptr)
    {
        m_mutex.lock();
        if (m_taskQ == nullptr)
        {
            m_taskQ = new TaskQueue;
        }
        m_mutex.unlock();
    }
    return m_taskQ;
}

但是實(shí)際上 m_taskQ = new TaskQueue; 在執(zhí)行過程中對(duì)應(yīng)的機(jī)器指令可能會(huì)被重新排序。正常過程如下:

  • 第一步:分配內(nèi)存用于保存 TaskQueue 對(duì)象。
  • 第二步:在分配的內(nèi)存中構(gòu)造一個(gè) TaskQueue 對(duì)象(初始化內(nèi)存)。
  • 第三步:使用 m_taskQ 指針指向分配的內(nèi)存。

但是被重新排序以后執(zhí)行順序可能會(huì)變成這樣:

  • 第一步:分配內(nèi)存用于保存 TaskQueue 對(duì)象。
  • 第二步:使用 m_taskQ 指針指向分配的內(nèi)存。
  • 第三步:在分配的內(nèi)存中構(gòu)造一個(gè) TaskQueue 對(duì)象(初始化內(nèi)存)。

這樣重排序并不影響單線程的執(zhí)行結(jié)果,但是在多線程中就會(huì)出問題。如果線程 A 按照第二種順序執(zhí)行機(jī)器指令,執(zhí)行完前兩步之后失去 CPU 時(shí)間片被掛起了,此時(shí)線程 B 在第 3 行處進(jìn)行指針判斷的時(shí)候 m_taskQ 指針是不為空的,但這個(gè)指針指向的內(nèi)存卻沒有被初始化,最后線程 B 使用了一個(gè)沒有被初始化的隊(duì)列對(duì)象就出問題了(出現(xiàn)這種情況是概率問題,需要反復(fù)的大量測(cè)試問題才可能會(huì)出現(xiàn))。

在 C++11 中引入了原子變量 atomic,通過原子變量可以實(shí)現(xiàn)一種更安全的懶漢模式的單例,代碼如下:

class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
        TaskQueue* queue = m_taskQ.load();  
        if (queue == nullptr)
        {
            // m_mutex.lock();  // 加鎖: 方式1
            lock_guard<mutex> locker(m_mutex);  // 加鎖: 方式2
            queue = m_taskQ.load();
            if (queue == nullptr)
            {
                queue = new TaskQueue;
                m_taskQ.store(queue);
            }
            // m_mutex.unlock();
        }
        return queue;
    }

    void print()
    {
        cout << "hello, world!!!" << endl;
    }
private:
    TaskQueue() = default;
    static atomic<TaskQueue*> m_taskQ;
    static mutex m_mutex;
};
atomic<TaskQueue*> TaskQueue::m_taskQ;
mutex TaskQueue::m_mutex;

int main()
{
    TaskQueue* queue = TaskQueue::getInstance();
    queue->print();
    return 0;
}

上面代碼中使用原子變量 atomic 的 store() 方法來存儲(chǔ)單例對(duì)象,使用 load() 方法來加載單例對(duì)象。在原子變量中這兩個(gè)函數(shù)在處理指令的時(shí)候默認(rèn)的原子順序是 memory_order_seq_cst(順序原子操作 - sequentially consistent),使用順序約束原子操作庫,整個(gè)函數(shù)執(zhí)行都將保證順序執(zhí)行,并且不會(huì)出現(xiàn)數(shù)據(jù)競(jìng)態(tài)(data races),不足之處就是使用這種方法實(shí)現(xiàn)的懶漢模式的單例執(zhí)行效率更低一些。

靜態(tài)局部對(duì)象

在實(shí)現(xiàn)懶漢模式的單例的時(shí)候,相較于雙重檢查鎖定模式有一種更簡(jiǎn)單的實(shí)現(xiàn)方法并且不會(huì)出現(xiàn)線程安全問題,那就是使用靜態(tài)局部局部對(duì)象,對(duì)應(yīng)的代碼實(shí)現(xiàn)如下:

class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
9        static TaskQueue taskQ;
10       return &taskQ;
    }
    void print()
    {
        cout << "hello, world!!!" << endl;
    }

private:
    TaskQueue() = default;
};

int main()
{
    TaskQueue* queue = TaskQueue::getInstance();
    queue->print();
    return 0;
}

在程序的第 9、10 行定義了一個(gè)靜態(tài)局部隊(duì)列對(duì)象,并且將這個(gè)對(duì)象作為了唯一的單例實(shí)例。使用這種方式之所以是線程安全的,是因?yàn)樵?C++11 標(biāo)準(zhǔn)中有如下規(guī)定,并且這個(gè)操作是在編譯時(shí)由編譯器保證的:

如果指令邏輯進(jìn)入一個(gè)未被初始化的聲明變量,所有并發(fā)執(zhí)行應(yīng)當(dāng)?shù)却撟兞客瓿沙跏蓟?/p>

最后總結(jié)一下懶漢模式和餓漢模式的區(qū)別:

懶漢模式的缺點(diǎn)是在創(chuàng)建實(shí)例對(duì)象的時(shí)候有安全問題,但這樣可以減少內(nèi)存的浪費(fèi)(如果用不到就不去申請(qǐng)內(nèi)存了)。餓漢模式則相反,在我們不需要這個(gè)實(shí)例對(duì)象的時(shí)候,它已經(jīng)被創(chuàng)建出來,占用了一塊內(nèi)存。對(duì)于現(xiàn)在的計(jì)算機(jī)而言,內(nèi)存容量都是足夠大的,這個(gè)缺陷可以被無視。

寫一個(gè)任務(wù)隊(duì)列?

首要任務(wù)就是設(shè)計(jì)一個(gè)單例模式的任務(wù)隊(duì)列,那么就需要賦予這個(gè)類一些屬性和方法:

屬性:

  • 存儲(chǔ)任務(wù)的容器,這個(gè)容器可以選擇使用 STL中的隊(duì)列(queue)
  • 互斥鎖,多線程訪問的時(shí)候用于保護(hù)任務(wù)隊(duì)列中的數(shù)據(jù)

方法:主要是對(duì)任務(wù)隊(duì)列中的任務(wù)進(jìn)行操作

  • 任務(wù)隊(duì)列中任務(wù)是否為空
  • 往任務(wù)隊(duì)列中添加一個(gè)任務(wù)
  • 從任務(wù)隊(duì)列中取出一個(gè)任務(wù)
  • 從任務(wù)隊(duì)列中刪除一個(gè)任務(wù)

根據(jù)分析,就可以把這個(gè)餓漢模式的任務(wù)隊(duì)列的單例類定義出來了:

#include <iostream>
#include <queue>
#include <mutex>
#include <thread>
using namespace std;

class TaskQueue
{
public:
    // = delete 代表函數(shù)禁用, 也可以將其訪問權(quán)限設(shè)置為私有
    TaskQueue(const TaskQueue& obj) = delete;
    TaskQueue& operator=(const TaskQueue& obj) = delete;
    static TaskQueue* getInstance()
    {
        return &m_obj;
    }
    // 任務(wù)隊(duì)列是否為空
    bool isEmpty()
    {
        lock_guard<mutex> locker(m_mutex);
        bool flag = m_taskQ.empty();
        return flag;
    }
    // 添加任務(wù)
    void addTask(int data)
    {
        lock_guard<mutex> locker(m_mutex);
        m_taskQ.push(data);
    }
    // 取出一個(gè)任務(wù)
    int takeTask()
    {
        lock_guard<mutex> locker(m_mutex);
        if (!m_taskQ.empty())
        {
            return m_taskQ.front();
        }
        return -1;
    }
    // 刪除一個(gè)任務(wù)
    bool popTask()
    {
        lock_guard<mutex> locker(m_mutex);
        if (!m_taskQ.empty())
        {
            m_taskQ.pop();
            return true;
        }
        return false;
    }
private:
    TaskQueue() = default;
    static TaskQueue m_obj;
    queue<int> m_taskQ;
    mutex m_mutex;
};
TaskQueue TaskQueue::m_obj;

int main()
{
    thread t1([]() {
        TaskQueue* taskQ = TaskQueue::getInstance();
        for (int i = 0; i < 100; ++i)
        {
            taskQ->addTask(i + 100);
            cout << "+++push task: " << i + 100 << ", threadID: " 
                << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(500));
        }
    });
    thread t2([]() {
        TaskQueue* taskQ = TaskQueue::getInstance();
        this_thread::sleep_for(chrono::milliseconds(100));
        while (!taskQ->isEmpty())
        {
            int data = taskQ->takeTask();
            cout << "---take task: " << data << ", threadID: " 
                << this_thread::get_id() << endl;
            taskQ->popTask();
            this_thread::sleep_for(chrono::seconds(1));
        }
    });
    t1.join();
    t2.join();
}

在上面的程序中有以下幾點(diǎn)需要說明一下:

正常情況下,任務(wù)隊(duì)列中的任務(wù)應(yīng)該是一個(gè)函數(shù)指針(這個(gè)指針指向的函數(shù)中有需要執(zhí)行的任務(wù)動(dòng)作),此處進(jìn)行了簡(jiǎn)化,用一個(gè)整形數(shù)代替了任務(wù)隊(duì)列中的任務(wù)。

任務(wù)隊(duì)列中的互斥鎖保護(hù)的是單例對(duì)象的中的數(shù)據(jù)也就是任務(wù)隊(duì)列中的數(shù)據(jù),上面所說的線程安全指的是在創(chuàng)建單例對(duì)象的時(shí)候要保證這個(gè)對(duì)象只被創(chuàng)建一次,和此處完全是兩碼事兒,需要區(qū)別看待。

12、公有成員,私有成員,保護(hù)成員

成員權(quán)限
  • 公有成員:public表明該數(shù)據(jù)成員、成員函數(shù)是對(duì)所有用戶開放的,所有用戶都可以直接進(jìn)行調(diào)用。
  • 私有成員:private為類的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),只能被本類的成員函數(shù)訪問,或者是友元訪問。
  • 保護(hù)成員:protected對(duì)于子女(繼承)、朋友(友元)來說,就是public的,可以自由使用,沒有任何限制,而對(duì)于其他的外部class,protected就變成private。
繼承
  • 公有繼承:繼承自父類的成員保持不變。
  • 私有繼承:繼承自父類的成員全部變?yōu)樗接谐蓡T。
  • 保護(hù)繼承:繼承自父類的公有成員變?yōu)楸Wo(hù)成員,其余不變。

13、友元函數(shù)和友元類

????????對(duì)象中的 protected 成員和private 成員不允許被非成員函數(shù)直接訪問,這稱為類的封裝性。為了外部訪問類內(nèi)的私有和保護(hù)數(shù)據(jù),我們可以定義友元。這么做事為了實(shí)現(xiàn)數(shù)據(jù)共享,提高執(zhí)行效率,同時(shí),又保有類的一定的封裝性和數(shù)據(jù)隱藏性。

友元可以是一個(gè)函數(shù),該函數(shù)稱為友元函數(shù)。友元也可以是一個(gè)類,該類被稱為友元類。

友元函數(shù)

類的友元函數(shù)是指在類定義的一個(gè)普通函數(shù),不是類的成員函數(shù),但是它可以自由地訪問類中的私有數(shù)據(jù)成員。它訪問對(duì)象中的成員必須通過對(duì)象名。

友元類

若一個(gè)類為另一個(gè)類的友元,則此類的所有成員都能訪問對(duì)方類的私有成員。

也就是,我認(rèn)你做朋友了,那么我的東西你隨便看隨便用,哪怕是私有的東西。認(rèn)你做朋友的方式,就是在我底下,把你聲明成是我的朋友。舉個(gè)例子如下:

C++:基礎(chǔ)知識(shí)

在 A 底下將 B 聲明為了友元類,那么 B 中實(shí)例化了一個(gè) A 類型的 a,a 中的私有變量就可以被訪問了。

主要注意的幾點(diǎn):友元關(guān)系不可以被繼承;友元關(guān)系是單向的;友元關(guān)系不能傳遞。文章來源地址http://www.zghlxwxcb.cn/news/detail-490884.html

到了這里,關(guān)于C++:基礎(chǔ)知識(shí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C語言基礎(chǔ)知識(shí):內(nèi)存分配

    目錄 內(nèi)存分配原理 內(nèi)存分配方法 靜態(tài)內(nèi)存分配 動(dòng)態(tài)內(nèi)存分配 MALLOC() CALLOC()/

    2024年02月07日
    瀏覽(35)
  • 【cuda】二、基礎(chǔ)知識(shí): 內(nèi)存管理 同步

    在CUDA中,使用** cudaMalloc() 來分配設(shè)備內(nèi)存,使用 cudaFree() **來釋放設(shè)備內(nèi)存。 cudaMallocManaged 統(tǒng)一內(nèi)存管理 **統(tǒng)一虛擬尋址(Unified Memory): 使用 cudaMallocManaged() **來分配可以在CPU和GPU之間共享的內(nèi)存。無需關(guān)心數(shù)據(jù)在主機(jī)或設(shè)備上。 cudaMallocManaged 是一個(gè)CUDA運(yùn)行時(shí)應(yīng)用程序接

    2024年01月18日
    瀏覽(20)
  • 操作系統(tǒng)基礎(chǔ)知識(shí)介紹之內(nèi)存層次結(jié)構(gòu)(一)

    操作系統(tǒng)基礎(chǔ)知識(shí)介紹之內(nèi)存層次結(jié)構(gòu)(一)

    傳統(tǒng)上,內(nèi)存層次結(jié)構(gòu)的設(shè)計(jì)者專注于優(yōu)化平均內(nèi)存訪問時(shí)間,這由緩存訪問時(shí)間、未命中率和未命中懲罰決定。 然而,最近,功率已成為主要考慮因素。 在高端微處理器中,可能有 60 MiB 或更多的片上高速緩存,并且大型二級(jí)或三級(jí)高速緩存將消耗大量功率。 這個(gè)問題在

    2024年02月04日
    瀏覽(22)
  • JVM基礎(chǔ)知識(shí)(內(nèi)存區(qū)域劃分,類加載,GC垃圾回收)

    JVM基礎(chǔ)知識(shí)(內(nèi)存區(qū)域劃分,類加載,GC垃圾回收)

    目錄 內(nèi)存區(qū)域劃分 JVM中的棧 JVM中的堆 程序計(jì)數(shù)器 方法區(qū)(元數(shù)據(jù)區(qū)) 給一段代碼,某個(gè)變量在哪個(gè)區(qū)域上? 類加載 類加載時(shí)機(jī) 雙親委派模型 GC 垃圾回收機(jī)制 GC 實(shí)際工作過程 1.找到垃圾/判定垃圾 1.可達(dá)性分析(Java中的做法) 2.引用計(jì)數(shù) 2.清理垃圾 1.標(biāo)記清除 2.復(fù)制算法 3.標(biāo)記整

    2024年02月07日
    瀏覽(28)
  • 自媒體運(yùn)營入門基礎(chǔ)知識(shí),掌握這4步,你也可以擁有百萬粉絲

    自媒體運(yùn)營入門基礎(chǔ)知識(shí),掌握這4步,你也可以擁有百萬粉絲

    第一步:申請(qǐng)賬號(hào) 確定好要做哪一個(gè)自媒體平臺(tái)后,你就需要到相應(yīng)的平臺(tái)進(jìn)行賬號(hào)的注冊(cè)申請(qǐng),在此給大家一個(gè)意見,千萬不要每個(gè)平臺(tái)都注冊(cè),建議注冊(cè)一到兩個(gè)平臺(tái),集中注意力,畢竟大家精力有限。 大家在申請(qǐng)賬號(hào)的時(shí)候,就需要填寫自己在平臺(tái)里面的用戶名和一

    2024年02月04日
    瀏覽(21)
  • 【C/C++】基礎(chǔ)知識(shí)之動(dòng)態(tài)申請(qǐng)內(nèi)存空間new-delete

    【C/C++】基礎(chǔ)知識(shí)之動(dòng)態(tài)申請(qǐng)內(nèi)存空間new-delete

    創(chuàng)作不易,本篇文章如果幫助到了你,還請(qǐng)點(diǎn)贊 關(guān)注支持一下???)!! 主頁專欄有更多知識(shí),如有疑問歡迎大家指正討論,共同進(jìn)步! ??c++系列專欄:C/C++零基礎(chǔ)到精通 ?? 給大家跳段街舞感謝支持!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? c語言內(nèi)容??:

    2024年02月07日
    瀏覽(29)
  • [C++]C++基礎(chǔ)知識(shí)概述

    [C++]C++基礎(chǔ)知識(shí)概述

    ? 目錄 C++基礎(chǔ)知識(shí)概述:: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1.什么是C++ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?2.C++發(fā)展史? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?3.C++ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?4.命名空間 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

    2023年04月16日
    瀏覽(27)
  • C++ | 語句的基礎(chǔ)知識(shí)(夯實(shí)基礎(chǔ))

    C++ | 語句的基礎(chǔ)知識(shí)(夯實(shí)基礎(chǔ))

    本篇文章主要介紹數(shù)據(jù)結(jié)構(gòu)中 C++ 的語句內(nèi)容,適合有 零基礎(chǔ) 的同學(xué),文中描述和代碼示例很詳細(xì),干貨滿滿,感興趣的小伙伴快來一起學(xué)習(xí)吧! ??大家好!我是新人博主朦朧的雨夢(mèng),希望大家多多關(guān)照和支持?????? ??大家一起努力,共同成長,相信我們都會(huì)遇到更好

    2024年02月05日
    瀏覽(22)
  • 【C++】C++模板基礎(chǔ)知識(shí)篇

    【C++】C++模板基礎(chǔ)知識(shí)篇

    個(gè)人主頁 : zxctscl 文章封面來自:藝術(shù)家–賢海林 如有轉(zhuǎn)載請(qǐng)先通知 實(shí)現(xiàn)一個(gè)通用的交換函數(shù): 在實(shí)現(xiàn)不同類型的參數(shù)Swap就得寫很多個(gè), 用起來太麻煩了。 使用函數(shù)重載雖然可以實(shí)現(xiàn),但是有一下幾個(gè)不好的地方: 重載的函數(shù)僅僅是類型不同,代碼復(fù)用率比較低,只要

    2024年03月28日
    瀏覽(21)
  • C++:基礎(chǔ)知識(shí)

    C++:基礎(chǔ)知識(shí)

    1、內(nèi)存四區(qū) 在C++程序執(zhí)行時(shí),內(nèi)存可以被劃分為以下四個(gè)區(qū)域: (1)代碼區(qū)(Code Segment ) :代碼區(qū)的聲明周期從程序加載到內(nèi)存開始,一直持續(xù)到程序結(jié)束。代碼區(qū)中存儲(chǔ)的是程序的 機(jī)器指令代碼 ,這些指令在程序執(zhí)行過程中被逐條執(zhí)行。代碼區(qū)的內(nèi)容是 只讀 的,不會(huì)

    2024年02月09日
    瀏覽(14)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包