文章目錄
- 46. 智能指針是什么?怎么使用?
- 1. std::unique_ptr
- 2. std::shared_ptr
- 3. std::weak_ptr
- 47. 什么是野指針?
- 1. 使用已釋放的指針
- 2. 未初始化的指針
- 3. 指針超出作用域
- 如何避免野指針
- 1. 立即將指針置空
- 2. 初始化指針
- 3. 使用智能指針
- 4. 避免返回局部變量的地址
- 48. 虛函數的機制是怎么實現的?
- 機制實現步驟
- 49. nullptr和null的區別?
- 50. 什么是二叉搜索樹?實際應用有什么?
46. 智能指針是什么?怎么使用?
C++11標準庫引入了三種主要的智能指針:std::unique_ptr、std::shared_ptr和std::weak_ptr。
1. std::unique_ptr
std::unique_ptr是一個獨占所有權的智能指針,它確保同一時間內只有一個指針可以擁有某個對象的所有權。適合用于一個對象只能被一個指針擁有的情況。
#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void display() {std::cout << "Display method of MyClass" << std::endl;}
};int main() {// 創建一個unique_ptr對象,管理MyClass實例std::unique_ptr<MyClass> ptr1(new MyClass());ptr1->display(); // 使用智能指針調用方法// 轉移所有權std::unique_ptr<MyClass> ptr2 = std::move(ptr1);if (!ptr1) {std::cout << "ptr1 is now null" << std::endl;}ptr2->display(); // 使用新的智能指針調用方法// ptr2超出作用域,MyClass對象自動銷毀return 0;
}
2. std::shared_ptr
std::shared_ptr是一個共享所有權的智能指針,可以被多個指針共享。它通過引用計數來管理對象的生命周期,當最后一個std::shared_ptr銷毀時,對象才會被釋放。
#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void display() {std::cout << "Display method of MyClass" << std::endl;}
};int main() {// 創建一個shared_ptr對象,管理MyClass實例std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();{// 創建第二個shared_ptr,共享相同的對象std::shared_ptr<MyClass> ptr2 = ptr1;ptr2->display(); // 使用智能指針調用方法std::cout << "ptr2 going out of scope" << std::endl;} // ptr2超出作用域,但對象未銷毀,因為ptr1還在使用ptr1->display(); // 使用剩余的智能指針調用方法// ptr1超出作用域,MyClass對象自動銷毀return 0;
}
3. std::weak_ptr
std::weak_ptr是一個不擁有所有權的智能指針,它是為了配合std::shared_ptr使用,解決循環引用問題。std::weak_ptr不能直接訪問對象,需要先轉換為std::shared_ptr。
#include <iostream>
#include <memory>class MyClass {
public:std::shared_ptr<MyClass> other;MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void display() {std::cout << "Display method of MyClass" << std::endl;}
};int main() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();// 使用weak_ptr避免循環引用ptr1->other = ptr2;ptr2->other = ptr1;ptr1->display();ptr2->display();// ptr1和ptr2超出作用域,MyClass對象自動銷毀return 0;
}
如果要使用 std::weak_ptr 來避免循環引用,可以對 MyClass 的 other 成員使用 std::weak_ptr,而不是 std::shared_ptr。這樣做可以防止形成循環引用,從而避免對象無法正確釋放的問題。下面是修改后的示例代碼:
#include <iostream>
#include <memory>class MyClass {
public:std::weak_ptr<MyClass> other; // 使用 weak_ptr 避免循環引用MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void display() {std::cout << "Display method of MyClass" << std::endl;}
};int main() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();// 使用 weak_ptr 來避免循環引用ptr1->other = ptr2;ptr2->other = ptr1;ptr1->display();ptr2->display();// ptr1 和 ptr2 超出作用域,MyClass 對象會正確釋放return 0;
}
當然,以下是一個總結 std::unique_ptr、std::shared_ptr 和 std::weak_ptr 三種智能指針特點的表格:
47. 什么是野指針?
野指針(Dangling Pointer)是指那些指向已經釋放或未分配內存的指針。使用野指針會導致未定義行為,包括程序崩潰、數據損壞等嚴重問題。
以下是關于野指針的詳細說明。
野指針的成因
1. 使用已釋放的指針
當一個指針指向的內存被釋放后,指針仍然保留原來的地址,此時指針變成野指針。
int* p = new int(10);
delete p;
// 此時,p成為野指針
// *p = 20; // 未定義行為,可能導致程序崩潰
2. 未初始化的指針
未初始化的指針默認指向一個不確定的地址,直接使用這種指針會導致野指針問題。
int* p; // p未初始化,指向一個隨機地址
// *p = 10; // 未定義行為,可能導致程序崩潰
3. 指針超出作用域
當指針指向的內存區域超出其作用域后,指針變成野指針。
int* foo() {int x = 10;return &x; // 返回局部變量的地址
}
// int* p = foo(); // p成為野指針,x已超出作用域
如何避免野指針
1. 立即將指針置空
在釋放內存后,立即將指針置為空指針(nullptr),避免使用野指針。
int* p = new int(10);
delete p;
p = nullptr; // 避免使用野指針
2. 初始化指針
聲明指針時,立即進行初始化。如果不知道該指向哪里,可以將其初始化為nullptr。
int* p = nullptr; // 初始化指針
// *p = 10; // 編譯錯誤,安全
3. 使用智能指針
智能指針(如std::unique_ptr和std::shared_ptr)可以自動管理內存,防止野指針。
#include <memory>void example() {std::unique_ptr<int> p = std::make_unique<int>(10);// 不需要手動釋放內存
}
4. 避免返回局部變量的地址
不要返回局部變量的地址,可以使用動態分配或者將結果通過參數返回。
int* foo() {int* x = new int(10); // 動態分配內存return x;
}void foo(int& x) {x = 10; // 通過參數返回
}
48. 虛函數的機制是怎么實現的?
機制實現步驟
#include <iostream>class Base {
public:virtual void show() {std::cout << "Base class show function\n";}
};class Derived : public Base {
public:void show() override {std::cout << "Derived class show function\n";}
};int main() {Base* b = new Derived();b->show(); // 將調用Derived類的show函數delete b;return 0;
}
49. nullptr和null的區別?
由于NULL是一個整數常量,使用NULL可能導致一些類型不安全的問題。例如:
void foo(int) { std::cout << "int" << std::endl; }
void foo(void*) { std::cout << "void*" << std::endl; }int main() {foo(NULL); // 調用 foo(int),而不是 foo(void*)cout<<NULL<<endl; //這里輸出0;return 0;
}
現在編譯器能發現問題,但是還是避開寫NULL
50. 什么是二叉搜索樹?實際應用有什么?
這種性質使得在BST中查找、插入和刪除元素非常高效,平均時間復雜度為 O(log n)。
6. 自動補全和拼寫檢查:
- 使用BST實現字符串的前綴樹,用于自動補全和拼寫檢查。
之后我會持續更新,如果喜歡我的文章,請記得一鍵三連哦,點贊關注收藏,你的每一個贊每一份關注每一次收藏都將是我前進路上的無限動力 !!!↖(▔▽▔)↗感謝支持!