C++_03

1、構造函數

1.1?什么是構造函數

? ? ? ? 類的構造函數是類的一種特殊的成員函數,它會在每次創建類的新對象時執行。

? ? ? ? 每次構造的是構造成員變量的初始化值,內存空間等。

? ? ? ? 構造函數的名稱與類的名稱是完全相同的,并且不會返回任何類型,也不會返回 void。構造函數可用于為某些成員變量設置初始值。

#include <iostream>
#include <string>using namespace std; // 使用std命名空間class Car {
public:string brand; // 不需要使用std::stringint year;// 無參構造函數Car() {brand = "未知";year = 0;cout << "無參構造函數被調用" << endl; // 不需要使用std::cout和std::endl}void display() {cout << "Brand: " << brand << ", Year: " << year << endl;}
};int main() {Car myCar; // 創建Car對象myCar.display(); // 顯示車輛信息return 0;
}

1.2?帶參數的構造函數

? ? ? ? 默認的構造函數沒有任何參數,但如果需要,構造函數也可以帶有參數。這樣在創建對象時就會給對象賦初始值。

#include <iostream>
#include <string>using namespace std;class Car {
public:string brand;int year;// 帶參數的構造函數,使用常規的賦值方式Car(string b, int y) {brand = b;year = y;}void display() {cout << "Brand: " << brand << ", Year: " << year << endl;}
};int main() {Car myCar("Toyota", 2020); // 使用帶參數的構造函數創建Car對象myCar.display(); // 顯示車輛信息return 0;
}

1.3?使用初始化列表

? ? ? ? 在C++中,使用初始化列表來初始化類的字段是一種高效的初始化方式,尤其在構造函數中。初始化列表直接在對象的構造過程中初始化成員變量,而不是先創建成員變量后再賦值。這對于提高性能尤其重要,特別是在涉及到復雜對象或引用和常量成員的情況下。

????????初始化列表緊跟在構造函數參數列表后面,以冒號( : )開始,后跟一個或多個初始化表達式,每個表達式通常用逗號分隔。下面是使用初始化列表初始化字段的例子:

class MyClass {
private:int a;double b;std::string c;public:// 使用初始化列表來初始化字段MyClass(int x, double y, const std::string& z) : a(x), b(y), c(z) {// 構造函數體}
};

? ? ? ? 在這個例子中, MyClass 有三個成員變量: a int 類型)、 b double 類型)和 c

std::string 類型)。當創建 MyClass 的一個實例時,我們通過構造函數傳遞三個參數,這些參數被用于通過初始化列表直接初始化成員變量。初始化列表 : a(x), b(y), c(z) 的意思是用 x 初始化
a ,用 y 初始化 b ,用 z 初始化 c
? ? ? ? 初始化列表的優點在于:
? ? ? ? (1)
效率 :對于非基本類型的對象,使用初始化列表比在構造函數體內賦值更高效,因為它避免了先默 認構造然后再賦值的額外開銷;
? ? ? ? (2) 必要性 :對于引用類型和常量類型的成員變量,必須使用初始化列表,因為這些類型的成員變量在 構造函數體內不能被賦值;
? ? ? ? (3)順序:成員變量的初始化順序是按照它們在類中聲明的順序,而不是初始化列表中的順序。

????????使用初始化列表是C++中推薦的初始化類成員變量的方式,因為它提供了更好的性能和靈活性。

1.4?拷貝構造函數

1.4.1?基本概念及發生條件

? ? ? ? 拷貝 構造函數是 C++ 中的一種特殊的構造函數,用于創建一個新對象作為現有對象的副本。它在以下幾 種情況下被調用:
? ? ? ? (1)當一個新對象被創建為另一個同類型的現有對象的副本時。例如: MyClass obj1 = obj2; MyClass obj1(obj2); ,其中 obj2 是現有的對象。
? ? ? ? (2)將對象作為參數傳遞給函數時(按值傳遞)。當對象作為參數傳遞給函數,并且參數不是引用時,會使用拷貝構造函數創建函數內部的對象副本。
? ? ? ? (3)從函數返回對象時(按值返回)。當函數返回對象,并且沒有使用引用或指針時,拷貝構造函數用于從函數返回值創建副本。
? ? ? ? (4)初始化數組或容器中的元素時。 例如,在創建一個包含對象的數組時,數組中的每個對象都是通過拷貝構造函數初始化的。
//其中, other 是對同類型對象的引用,通常是常量引用。
class MyClass {
public:MyClass(const MyClass& other);
};
#include <iostream>
#include <string>using namespace std;class Car {
public:string brand;int year;// 常規構造函數Car(string b, int y) : brand(b), year(y) {}// 拷貝構造函數Car(const Car& other) {brand = other.brand;year = other.year;cout << "拷貝構造函數被調用" << endl;}void display() {cout << "Brand: " << brand << ", Year: " << year << endl;}
};int main() {Car car1("Toyota", 2020); // 使用常規構造函數Car car2 = car1; // 使用拷貝構造函數car1.display();car2.display();return 0;
}

1.4.2?淺拷貝

? ? ? ? 淺拷貝只復制對象的成員變量的值。如果成員變量是指針,則復制指針的值(即內存地址),而不是指針所指向的實際數據。這會導致多個對象共享相同的內存地址。

#include <iostream>using namespace std;class Shallow {
public:int* data;Shallow(int d) {//(d):這是初始化表達式。在這里,分配的 int 類型內存被初始化為 d 的值。如果 d 的值是20,那么分配的內存將被初始化為 20。data = new int(d); // 動態分配內存cout << "觀察數據:" << endl;cout << d << endl;cout << *data << endl;cout << "觀察內存在構造函數中:" << endl;cout << data << endl;}// 默認的拷貝構造函數是淺拷貝~Shallow() {delete data; // 釋放內存}
};int main() {Shallow obj1(20);Shallow obj2 = obj1; // 淺拷貝cout << "觀察內存在main函數obj2的data地址:" << endl;cout << obj2.data << endl;cout << "obj1 data: " << *obj1.data << ", obj2 data: " << *obj2.data << endl;return 0;
}

? ? ? ? 在這個例子中, obj2 是通過淺拷貝 obj1 創建的。這意味著 obj1.data obj2.data 指向相同的內存地址。obj1 obj2 被銷毀時,同一內存地址會被嘗試釋放兩次,導致潛在的運行時錯誤。 在Linux中我們獲得如下運行結果:

1.4.3?深拷貝

? ? ? ? 深拷貝復制對象的成員變量的值以及指針所指向的實際數據。這意味著創建新的獨立副本,避免了共享內存地址的問題。

#include <iostream>using namespace std;class Deep {
public:int* data;Deep(int d) {data = new int(d); // 動態分配內存cout << "觀察數據:" << endl;cout << d << endl;cout << *data << endl;cout << "觀察內存在構造函數中:" << endl;cout << data << endl;}// 顯式定義深拷貝的拷貝構造函數Deep(const Deep& source) {data = new int(*source.data); // 復制數據,而不是地址cout << "深拷貝構造函數\n";}~Deep() {delete data; // 釋放內存}
};int main() {Deep obj1(20);Deep obj2 = obj1; // 深拷貝cout << "觀察內存在main函數obj2的data地址:" << endl;cout << obj2.data << endl;cout << "obj1 data: " << *obj1.data << ", obj2 data: " << *obj2.data << endl;return 0;
}

? ? ? ? 在這個例子中, obj2 是通過深拷貝 obj1 創建的。這意味著 obj1.data obj2.data 指向不同的內存地址。每個對象有自己的內存副本,因此不會相互影響,避免了潛在的運行時錯誤。

1.4.4?規則三則

? ? ? ? ?在C++中,規則三則(Rule of Three)是一個面向對象編程原則,它涉及到類的拷貝控制。規則三則指出,如果你需要顯式地定義或重載類的任何一個拷貝控制操作(拷貝構造函數、拷貝賦值運算符、析構函數),那么你幾乎肯定需要顯式地定義或重載所有三個。這是因為這三個功能通常都是用于管理動態分配的資源,比如在堆上分配的內存。

#include <iostream>
#include <cstring>class MyClass {
private:char* buffer;public:// 構造函數MyClass(const char* str) {if (str) {buffer = new char[strlen(str) + 1];strcpy(buffer, str);} else {buffer = nullptr;}}// 析構函數~MyClass() {delete[] buffer;}// 拷貝構造函數MyClass(const MyClass& other) {if (other.buffer) {buffer = new char[strlen(other.buffer) + 1];strcpy(buffer, other.buffer);} else {buffer = nullptr;}}// 拷貝賦值運算符MyClass& operator=(const MyClass& other) {if (this != &other) {delete[] buffer; // 首先刪除當前對象的資源if (other.buffer) {buffer = new char[strlen(other.buffer) + 1];strcpy(buffer, other.buffer);} else {buffer = nullptr;}}return *this;}
};int main() {MyClass obj1("Hello");MyClass obj2 = obj1; // 調用拷貝構造函數MyClass obj3("World");obj3 = obj1; // 調用拷貝賦值運算符return 0;
}

? ? ? ? 在這個例子中,構造函數為成員變量 buffer 分配內存,并復制給定的字符串;析構函數釋放 buffer 所占用的內存,以避免內存泄露;拷貝構造函數創建一個新對象作為另一個現有對象的副本,并為其分配新的內存,以避免多個對象共享同一內存;拷貝賦值運算符更新對象時,首先釋放原有資源,然后根據新對象的狀態分配新資源。

????????這個類遵循規則三則,確保了動態分配資源的正確管理,避免了內存泄露和淺拷貝問題。

1.4.5?避免不必要的拷貝

? ? ? ? 避免不必要的拷貝是 C++ 程序設計中的一個重要原則,尤其是在處理大型對象或資源密集型對象時。使用引用(包括常量引用)和移動語義(C++11 引入)是實現這一目標的兩種常見方法。

1.4.5.1 使用引用傳遞對象

? ? ? ? 通過使用引用(尤其是常量引用)來傳遞對象,可以避免在函數調用時創建對象的副本。

#include <iostream>
#include <vector>using namespace std;class LargeObject {// 假設這是一個占用大量內存的大型對象
};void processLargeObject(const LargeObject& obj) {// 處理對象,但不修改它cout << "Processing object..." << endl;
}int main() {LargeObject myLargeObject;processLargeObject(myLargeObject); // 通過引用傳遞,避免拷貝return 0;
}

? ? ? ? 在這個例子中, processLargeObject 函數接受一個對 LargeObject 類型的常量引用,避免了在函數調用時復制整個 LargeObject

1.4.5.2?使用移動語義

? ? ? ? C++11 引入了移動語義,允許資源(如動態分配的內存)的所有權從一個對象轉移到另一個對象,這避免了不必要的拷貝。

#include <iostream>
#include <utility> // 對于 std::moveusing namespace std;class MovableObject {
public:MovableObject() {// 構造函數}MovableObject(const MovableObject& other) {// 拷貝構造函數(可能很昂貴)}MovableObject(MovableObject&& other) noexcept {// 移動構造函數(輕量級)// 轉移資源的所有權}MovableObject& operator=(MovableObject&& other) noexcept {// 移動賦值運算符// 轉移資源的所有權return *this;}
};MovableObject createObject() {MovableObject obj;return obj; // 返回時使用移動語義,而非拷貝
}int main() {MovableObject obj = createObject(); // 使用移動構造函數return 0;
}

? ? ? ? 在這個例子中, MovableObject 類有一個移動構造函數和一個移動賦值運算符,它們允許對象的資源(如動態分配的內存)在賦值或返回時被“移動而非復制。這減少了對資源的不必要拷貝,提高了效率。通過這些方法,可以在 C++ 程序中有效地減少不必要的對象拷貝,尤其是對于大型或資源密集型的對象。

1.4.6 拷貝構造函數的隱式調用

? ? ? ? 在C++ 中,拷貝構造函數可能會在幾種不明顯的情況下被隱式調用。這種隱式調用通常發生在對象需要被復制時,但代碼中并沒有明顯的賦值或構造函數調用。了解這些情況對于高效和正確地管理資源非常重要。

1.4.6.1?作為函數參數傳遞(按值傳遞)

? ? ? ? 當對象作為函數參數按值傳遞時,會調用拷貝構造函數來創建參數的本地副本。

#include <iostream>using namespace std;class MyClass {
public:MyClass() {}MyClass(const MyClass &) {cout << "拷貝構造函數被隱式調用" << endl;}
};void function(MyClass obj) {// 對 obj 的操作
}int main() {MyClass myObject;function(myObject); // 調用 function 時,拷貝構造函數被隱式調用return 0;
}
1.4.6.2?從函數返回對象(按值返回)

? ? ? ? 當函數返回一個對象時,拷貝構造函數會被用于創建返回值的副本。

MyClass function() {MyClass tempObject;return tempObject; // 返回時,拷貝構造函數被隱式調用
}int main() {MyClass myObject = function(); // 接收返回值時可能還會有一次拷貝(或移動)return 0;
}
1.4.6.3?初始化另一個對象

? ? ? ? 當用一個對象初始化另一個同類型的新對象時,會使用拷貝構造函數。

int main() {MyClass obj1;MyClass obj2 = obj1; // 初始化時,拷貝構造函數被隱式調用return 0;
}

? ? ? ? 在所有這些情況下,如果類包含資源管理(例如,動態內存分配),那么正確地實現拷貝構造函數是非常重要的,以確保資源的正確復制和管理,防止潛在的內存泄漏或其他問題。此外,隨著 C++11 的引入,移動語義提供了對資源的高效管理方式,可以減少這些場景中的資源復制。

1.4.7?禁用拷貝構造函數

? ? ? ? 在C++ 中,禁用拷貝構造函數是一種常用的做法,尤其是在設計那些不應該被復制的類時。這可以通過將拷貝構造函數聲明為 private 或使用 C++11 引入的 delete 關鍵字來實現。這樣做的目的是防止類的對象被拷貝,從而避免可能導致的問題,如資源重復釋放、無意義的資源復制等。

1.4.7.1?使用?delete?關鍵字

? ? ? ? C++11 及更高版本中,可以使用 delete 關鍵字明確指定不允許拷貝構造,這種方法清晰明了,它向編譯器和其他程序員直接表明該類的對象不能被拷貝。

class NonCopyable {
public:NonCopyable() = default; // 使用默認構造函數// 禁用拷貝構造函數NonCopyable(const NonCopyable&) = delete;// 禁用拷貝賦值運算符NonCopyable& operator=(const NonCopyable&) = delete;
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 編譯錯誤,拷貝構造函數被禁用return 0;
}
1.4.7.2?使用 private 聲明(C++98/03

? ? ? ? 在C++11 之前,常見的做法是將拷貝構造函數和拷貝賦值運算符聲明為 private ,并且不提供實現:

class NonCopyable {
private:// 將拷貝構造函數和拷貝賦值運算符設為私有NonCopyable(const NonCopyable&);NonCopyable& operator=(const NonCopyable&);public:NonCopyable() {}
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 編譯錯誤,因為無法訪問私有的拷貝構造函數return 0;
}

? ? ? ? ?在這個例子中,任何嘗試拷貝 NonCopyable 類型對象的操作都會導致編譯錯誤,因為拷貝構造函數和拷貝賦值運算符是私有的,外部代碼無法訪問它們。

????????通過這些方法,可以確保類的對象不會被意外地拷貝,從而避免可能出現的資源管理相關的錯誤。

1.4.8?小結

? ? ? ? 在C++ 中拷貝構造函數需要注意的要點:

?要點

描述
定義和作用
拷貝構造函數在創建對象作為另一個現有對象副本時調用, 通常有一個對同 類型對象的常量引用參數。
語法
典型聲明為 ClassName(const ClassName &other)
深拷貝與淺拷貝
淺拷貝復制值,深拷貝創建資源的獨立副本。對于包含指針的類,深拷貝通常必要。
規則三則 (Rule of
Three)
如果實現了拷貝構造函數、拷貝賦值運算符或析構函數中的任何一個,通常應該實現所有三個。
避免不必要的拷貝
對于大型對象,使用移動語義避免不必要的拷貝,并在傳遞對象時使用引用或指針。
拷貝構造函數的隱 式調用
不僅在顯式復制時調用,也可能在將對象作為函數參數傳遞、從函數返回對象時隱式調用。
禁用拷貝構造函數
對于某些類,可以通過將拷貝構造函數聲明為私有或使用 delete 關鍵字 禁用拷貝。

1.5?this?關鍵字

? ? ? ? 在C++ 中, this 關鍵字是一個指向調用對象的指針。它在成員函數內部使用,用于引用調用該函數的對象。使用 this 可以明確指出成員函數正在操作的是哪個對象的數據成員。下面是一個使用 Car 類來展示 this 關鍵字用法的示例:

#include <iostream>
#include <string>using namespace std;class Car {
private:string brand;int year;public:Car(string brand, int year) {this->brand = brand;this->year = year;// cout << "構造函數中:" << endl;// cout << this << endl;}void display() const {cout << "Brand: " << this->brand << ", Year: " << this->year << endl;// 也可以不使用 this->,直接寫 brand 和 year}Car& setYear(int year) {this->year = year; // 更新年份return *this; // 返回調用對象的引用}
};int main()
{Car car("寶馬",2024);car.display();// 鏈式調用car.setYear(2023).display();// cout << "main函數中:" << endl;// cout << &car << endl;// Car car2("寶馬",2024);// cout << "main函數中:" << endl;// cout << &car2 << endl;return 0;
}

? ? ? ? 在這個例子中, Car 類的構造函數使用 this 指針來區分成員變量和構造函數參數。同樣, setYear成員函數使用 this 指針來返回調用該函數的對象的引用,這允許鏈式調用,如myCar.setYear(2021).display(); 。在 main 函數中創建了 Car 類型的對象,并展示了如何使用這些成員函數。

1.6?new/delete?關鍵字

? ? ? ? 在C++中, new 關鍵字用于動態分配內存。它是C++中處理動態內存分配的主要工具之一,允許在程序運行時根據需要分配內存。

? ? ? ? 基本用法:

? ? ? ? (1)分配單個對象:使用 new 可以在堆上動態分配一個對象。例如, new int 會分配一個 int 類型的空間,并返回一個指向該空間的指針。

int* ptr = new int; //C語言中,int *p = (int *)malloc(sizeof(int));

? ? ? ? (2)分配對象數組: new 也可以用來分配一個對象數組。例如, new int[10] 會分配一個包含10個整數的 數組。

int* arr = new int[10]; //C語言中,int *arr = (int *)malloc(sizeof(int)*10);

? ? ? ? (3)初始化:可以在 new 表達式中使用初始化。對于單個對象,可以使用構造函數的參數。

MyClass* obj = new MyClass(arg1, arg2);

? ? ? ? 在使用 new 分配的內存必須顯式地通過 delete (對于單個對象)或 delete[] (對于數組)來釋放,以避免內存泄露:

? ? ? ? (1)釋放單個對象:

delete ptr; // 釋放 ptr 指向的對象

? ? ? ? (2)釋放數組:

delete[] arr; // 釋放 arr 指向的數組

? ? ? ? 注:(1)異常安全:如果 new 分配內存失敗,它會拋出 std::bad_alloc 異常(除非使用了 nothrow 版本);

? ? ? ? (2)內存泄露:忘記釋放使用 new 分配的內存會導致內存泄露;

? ? ? ? (3)匹配使用 delete delete[] :為避免未定義行為,使用 new 分配的單個對象應該使用delete 釋放,使用 new[] 分配的數組應該使用 delete[] 釋放。

class MyClass {
public:MyClass() {std::cout << "Object created" << std::endl;}
};int main() {// 分配單個對象MyClass* myObject = new MyClass();// 分配對象數組int* myArray = new int[5]{1, 2, 3, 4, 5};// 使用對象和數組...// 釋放內存delete myObject;delete[] myArray;return 0;
}

? ? ? ? 在這個例子中, new 被用來分配一個 MyClass 類型的對象和一個整數數組,然后使用 delete 和delete[] 來釋放內存。每個 new 都對應一個 delete ,保證了動態分配的內存被適當管理。

2、析構函數

? ? ? ? 析構函數是C++中的一個特殊的成員函數,它在對象生命周期結束時被自動調用,用于執行對象銷毀前的清理工作。析構函數特別重要,尤其是在涉及動態分配的資源(如內存、文件句柄、網絡連接等)的情況下。

? ? ? ? 基本特性:

? ? ? ? (1)名稱:析構函數的名稱由波浪號( ~ )后跟類名構成,如 ~MyClass()

? ? ? ? (2)無返回值和參數:析構函數不接受任何參數,也不返回任何值。

? ? ? ? (3)自動調用:當對象的生命周期結束時(例如,一個局部對象的作用域結束,或者使用 delete 刪除一個動態分配的對象),析構函數會被自動調用。

? ? ? ? (4)不可重載:每個類只能有一個析構函數。

? ? ? ? (5)繼承和多態:如果一個類是多態基類,其析構函數應該是虛的。

#include <iostream>using namespace std;class MyClass{
private:int* datas;public:MyClass(int size){datas = new int[size];}~MyClass(){cout << "析構函數被調用" << endl;delete[] datas;}
};int main()
{MyClass m1(5);MyClass *m2 = new MyClass(10);delete m2;return 0;
}

? ? ? ? ?在這個示例中, MyClass 的構造函數分配了一塊內存,而析構函數釋放了這塊內存。當 obj 的生命周期結束時(即離開了它的作用域), MyClass 的析構函數被自動調用,負責清理資源,防止內存泄露。

? ? ? ? 析構函數在管理資源方面非常重要。沒有正確實現析構函數,可能導致資源泄露或其他問題。在基于RAII(資源獲取即初始化)原則的C++編程實踐中,確保資源在對象析構時被適當釋放是非常關鍵的。當使用智能指針和其他自動資源管理技術時,可以減少顯式編寫析構函數的需要,但了解析構函數的工作原理仍然很重要。

? ? ? ? 關于析構函數的要點:

要點
描述
定義和作
析構函數在對象生命周期結束時自動調用,用于清理對象可能持有的資源。
語法
析構函數名稱由波浪線 (~) 后跟類名構成,例如 MyClass 的析構函數為~MyClass() 。
資源管理
用于釋放對象在生命周期中分配的資源,如動態內存、文件句柄、網絡連接等。
自動調用機制
當對象離開其作用域或通過 delete 刪除時,將自動調用其析構函數。
防止資源 泄露
正確實現析構函數對防止資源泄露至關重要,特別是在涉及動態資源分配的情況。
虛析構函數
如果類作為基類設計,應有一個虛析構函數,以確保正確調用派生類的析構函數。
析構函數與異常
析構函數不應拋出異常,如果可能拋出,應在函數內捕獲。
刪除的析構函數
可以通過將析構函數聲明為刪除( ~MyClass() = delete; )來禁止刪除某類對象。
與構造函 數的關系
每個類只能有一個析構函數,不可重載,與構造函數相比。
規則三則/ 五則
如果類需要自定義析構函數、拷貝構造函數或拷貝賦值運算符,可能也需要自定義另外兩個(規則三則)。在 C++11 后還包括移動構造函數和移動賦值運算符(規則五則)。

3、靜態成員

3.1?靜態成員的定義

? ? ? ? 靜態成員在C++類中是一個重要的概念,它包括靜態成員變量和靜態成員函數。靜態成員的特點和存在的意義如下:

? ? ? ? 靜態成員變量:

? ? ? ? (1)定義:靜態成員變量是類的所有對象共享的變量。與普通成員變量相比,無論創建了多少個類的實例,靜態成員變量只有一份拷貝。

? ? ? ? (2)初始化:靜態成員變量需要在類外進行初始化,通常在類的實現文件中。

? ? ? ? (3)訪問:靜態成員變量可以通過類名直接訪問,不需要創建類的對象。也可以通過類的對象訪問。

? ? ? ? (4)用途:常用于存儲類級別的信息(例如,計數類的實例數量)或全局數據需要被類的所有實例共享。

? ? ? ? 靜態成員函數:

? ? ? ? (1)定義:靜態成員函數是可以不依賴于類的實例而被調用的函數。它不能訪問類的非靜態成員變量和非靜態成員函數。

? ? ? ? (2)訪問:類似于靜態成員變量,靜態成員函數可以通過類名直接調用,也可以通過類的實例調用。

? ? ? ? (3)用途:常用于實現與具體對象無關的功能,或訪問靜態成員變量。

class MyClass {
public:static int staticValue; // 靜態成員變量MyClass() {// 每創建一個對象,靜態變量增加1staticValue++;}static int getStaticValue() {// 靜態成員函數return staticValue;}
};// 類外初始化靜態成員變量
int MyClass::staticValue = 0;int main() {MyClass obj1, obj2;std::cout << MyClass::getStaticValue(); // 輸出2
}

? ? ? ? 靜態成員的優點:

? ? ? ? (1)共享數據:允許對象之間共享數據,而不需要每個對象都有一份拷貝。

? ? ? ? (2)節省內存:對于頻繁使用的類,使用靜態成員可以節省內存。

? ? ? ? (3)獨立于對象的功能:靜態成員函數提供了一種在不創建對象的情況下執行操作的方法,這對于實現工具函數或管理類級別狀態很有用。

3.2?靜態成員變量的作用

? ? ? ? 靜態成員變量在C++中的一個典型應用是用于跟蹤類的實例數量。這個案例體現了靜態成員變量的特性:它們在類的所有實例之間共享,因此適合于存儲所有實例共有的信息。

#include <iostream>using namespace std;class Myclass{
private:static int staticNumofInstance;public:Myclass(){staticNumofInstance++;}~Myclass(){staticNumofInstance--;}static int getNunofInstance(){return staticNumofInstance;}
};int Myclass::staticNumofInstance = 0;int main()
{Myclass m1;cout << Myclass::getNunofInstance() << endl;Myclass m2;cout << m2.getNunofInstance() << endl;{Myclass m3;cout << Myclass::getNunofInstance() << endl;Myclass m4;cout << Myclass::getNunofInstance() << endl;}cout << Myclass::getNunofInstance() << endl;Myclass *m5 = new Myclass;cout << Myclass::getNunofInstance() << endl;delete m5;cout << Myclass::getNunofInstance() << endl;return 0;
}

? ? ? ? 在這個例子中: Myclass 類有一個靜態成員變量 staticNumofInstance ,用來跟蹤該類的實例數量。每當創建 Myclass 的新實例時,構造函數會增加 staticNumofInstance 。每當一個 Myclass 實例被銷毀時,析構函數會減少 staticNumofInstance 。通過靜態成員函數 getNunofInstance 可以隨時獲取當前的實例數量。靜態成員變量 staticNumofInstance 在類外初始化為0

????????這個案例展示了靜態成員變量如何在類的所有實例之間共享,并為所有實例提供了一個共同的狀態(在這個例子中是實例的數量)。這種技術在需要跟蹤對象數量或實現某種形式的資源管理時特別有用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/39432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/39432.shtml
英文地址,請注明出處:http://en.pswp.cn/web/39432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MODBUS TCP協議簡介

目錄 一、協議概述 二、協議結構 三、功能碼 四、通信過程 五、注意事項 六、應用實例 七、優點 八、缺點 MODBUS TCP協議是一種基于TCP/IP協議的Modbus變種&#xff0c;它允許Modbus協議在以太網網絡上運行&#xff0c;使得設備之間可以通過IP網絡交換數據。以下是MOD…

Windows系統安裝SSH服務結合內網穿透配置公網地址遠程ssh連接

前言 在當今的數字化轉型時代&#xff0c;遠程連接和管理計算機已成為日常工作中不可或缺的一部分。對于 Windows 用戶而言&#xff0c;SSH&#xff08;Secure Shell&#xff09;協議提供了一種安全、高效的遠程訪問和命令執行方式。SSH 不僅提供了加密的通信通道&#xff0c;…

路由的高級用法

多級路由 1.新建一個Mian組件 <template><div> <h1>我是Msg的子組件</h1></div> </template><script> export default {name: "Mian", } </script><style> </style> 2.在router中msg小新建一個路由 imp…

Canvas合集更更更之實現由畫布中心向外隨機不斷發散的粒子效果

實現效果 1.支持顏色設置 2.支持粒子數量設置 3.支持粒子大小設置 寫在最后&#x1f352; 源碼&#xff0c;關注&#x1f365;蘇蘇的bug&#xff0c;&#x1f361;蘇蘇的github&#xff0c;&#x1f36a;蘇蘇的碼云

java中各種數據類型和集合的判空(代碼演示+工具類)

目錄 基本數據類型 對象類型 集合類型 綜合示例 總結 工具類 hutool 基本數據類型 基本數據類型在Java中不能為null&#xff0c;它們有默認值。基本數據類型包括&#xff1a; intfloatdoublecharbooleanbyteshortlong 因此&#xff0c;對基本數據類型不需要進行判空檢…

實驗九 存儲過程和觸發器

題目 創建并執行一個無參數的存儲過程proc_product1&#xff0c;通過該存儲過程可以查詢商品類別名稱為“筆記本電腦”的商品的詳細信息&#xff1a;包括商品編號、商品名稱、品牌、庫存量、單價和上架時間信息 2、創建并執行一個帶輸入參數的存儲過程proc_product2&#xff…

【軟件測試】Postman接口測試基本操作

&#x1f345; 視頻學習&#xff1a;文末有免費的配套視頻可觀看 &#x1f345; 點擊文末小卡片 &#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;薪資嘎嘎漲 Postman-獲取驗證碼 需求&#xff1a;使用Postman訪問驗證碼接口&#xff0c;并查看響應結果…

圖書管理系統(持久化存儲數據以及增添新功能)

目錄 一、數據庫表設計 二、引入MyBatis 和MySQL 驅動依賴 三、配置數據庫 & 日志 四、Model創建 五、枚舉類 常量類用戶登錄 六、用戶登錄 七、添加圖書 八、圖書列表 九、修改圖書 十、刪除圖書 十一、批量刪除 十二、強制登錄 十三、前端代碼 &#xff0…

AI與測試相輔相成

AI助力軟件測試 1.AI賦能軟件測試 使用AI工具來幫助測試人員提高測試效率&#xff0c;提供缺陷分析和缺陷預測。 語法格式 設定角色 具體指示 上下文格式 例: 角色&#xff1a;你是一個測試人員 內容&#xff1a;請幫我生成登錄案例的測試用例 ? 1.只有輸入正確賬號和密碼才…

生命在于學習——Python人工智能原理(3.2.1)

二、隨機變量 2.1 隨機變量及其分布 &#xff08;一&#xff09;基本概念 定義1 隨機變量 隨機變量表示隨機試驗各種結果的實值單值函數&#xff0c;即能用數學分析方法來研究隨機現象&#xff0c;例如某一時間內公共汽車站等車的乘客人數、淘寶在一定時間內的交易次數等&am…

Shenandoah GC概述

文章目錄 1_介紹2_原理1.0版本2.0版本3_ShenandoahGC的執行流程4_并發轉移階段 – 并發問題 1_介紹 Shenandoah 是由Red Hat開發的一款低延遲的垃圾收集器&#xff0c;Shenandoah 并發執行大部分 GC 工作&#xff0c;包括并發的整理&#xff0c;堆大小對STW的時間基本沒有影響…

if __name__ == “__main__“

在Python中&#xff0c;if __name__ "__main__": 這行代碼非常常見&#xff0c;它用于判斷當前運行的腳本是否是主程序。這里的 __name__ 是一個特殊變量&#xff0c;當Python文件被直接運行時&#xff0c;__name__ 被自動設置為字符串 "__main__"。但是&…

【pearcmd】通過pearcmd.php 進行GetShell

https://cloud.tencent.com/developer/article/2204400 關于PHP 配置 register_argc_argv 小結 的一些研究文章。 應用例題 [NewStarCTF 2023 公開賽道]Include &#x1f350; <?phperror_reporting(0);if(isset($_GET[file])) {$file $_GET[file];if(preg_match(/flag|l…

如何理解synchronized鎖升級

在Java中&#xff0c;synchronized 關鍵字是實現線程同步的一種方式&#xff0c;它涉及到鎖的升級和釋放的過程。理解synchronized 鎖的升級可以分為三個階段&#xff1a;無鎖狀態、偏向鎖狀態和輕量級鎖狀態。 無鎖狀態&#xff1a; 當對象被創建時&#xff0c;默認處于無鎖狀…

貪心 | Java | LeetCode 455, 376, 53 做題總結

貪心算法介紹 貪心算法&#xff1a;貪心的本質是選擇每一階段的局部最優&#xff0c;從而達到全局最優。 說實話貪心算法并沒有固定的套路。 一般解題步驟 貪心算法一般分為如下四步&#xff1a; ① 將問題分解為若干個子問題 ② 找出適合的貪心策略 ③ 求解每一個子問題的…

SQL Server數據庫的組成

《SQL Server 2022從入門到精通&#xff08;視頻教學超值版&#xff09;》圖書介紹-CSDN博客 對于數據庫的概念&#xff0c;沒有一個完全固定的定義&#xff0c;隨著數據庫歷史的發展&#xff0c;定義的內容也有很大的差異&#xff0c;其中一種比較普遍的觀點認為&#xff0c;…

Java中的并行計算與任務分發策略

Java中的并行計算與任務分發策略 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 并行計算的重要性與挑戰 在當今軟件開發領域&#xff0c;隨著數據量和計算復…

c++獲取路徑中的文件名

C獲取路徑中的文件名有狠多方法&#xff0c;最常見的方法&#xff1a; 使用C標準庫 首先&#xff0c;可以使用C標準庫中的字符串處理函數來獲取路徑中的文件名。可以通過以下步驟實現&#xff1a; 使用字符串分割函數&#xff08;例如std::string::find_last_of、std::string…

Winform中使用HttpClient實現調用http的post接口并設置傳參content-type為application/json示例

場景 Winform中怎樣使用HttpClient調用http的get和post接口并將接口返回json數據解析為實體類&#xff1a; Winform中怎樣使用HttpClient調用http的get和post接口并將接口返回json數據解析為實體類_winform解析json-CSDN博客 上面使用HttpClient調用post接口時使用的HttpCon…

21.《C語言》——【位操作符】

&#x1f33b;開場語 親愛的讀者&#xff0c;大家好&#xff01;我是一名正在學習編程的高校生。在這個博客里&#xff0c;我將和大家一起探討編程技巧、分享實用工具&#xff0c;并交流學習心得。希望通過我的博客&#xff0c;你能學到有用的知識&#xff0c;提高自己的技能&a…