C++中的智能指针类别
目录
摘要
std::unique_ptr
std::weak_ptr
补充:
std::make_unique
其它
管理动态内存
总结
摘要
C++中的智能指针是用来管理动态内存的对象,避免我们手动管理内存时可能会带来的复杂性和错误。C++标准库提供了几种不同类型的智能指针,包括`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`。
std::unique_ptr
1. `std::unique_ptr`是独占所有权的智能指针,即同一时间只能有一个`std::unique_ptr`指向特定的对象。
2. 当`std::unique_ptr`被销毁时,它所管理的对象会被自动释放。
3. 不能复制`std::unique_ptr`,只能移动它。
4. 当`std::unique_ptr`超出作用域时,它会自动释放资源。
5. 避免从原始指针创建多个`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 show() {std::cout << "MyClass show" << std::endl;}
};void uniquePtrExample() {std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();ptr->show();// 移动所有权std::unique_ptr<MyClass> ptr2 = std::move(ptr);if (!ptr) {std::cout << "ptr is null after move" << std::endl;}ptr2->show();
}int main() {uniquePtrExample();return 0;
}
std::shared_ptr
1. `std::shared_ptr`是共享所有权的智能指针,即可以有多个`std::shared_ptr`指向同一个对象。
2. 当最后一个`std::shared_ptr`被销毁时,对象会被自动释放。
3. `std::shared_ptr`使用引用计数来管理对象,当引用计数为零时,释放对象。
4. 避免循环引用(Circular Reference),这会导致内存泄漏。使用`std::weak_ptr`解决这个问题。
5. 使用`std::make_shared`创建`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 show() {std::cout << "MyClass show" << std::endl;}
};void sharedPtrExample() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();{std::shared_ptr<MyClass> ptr2 = ptr1;ptr2->show();std::cout << "Reference count: " << ptr1.use_count() << std::endl;}std::cout << "Reference count after scope: " << ptr1.use_count() << std::endl;
}int main() {sharedPtrExample();return 0;
}
std::weak_ptr
1. `std::weak_ptr`是一个不拥有所有权的智能指针,它用于监视`std::shared_ptr`的生命周期。
2. `std::weak_ptr`不会影响`std::shared_ptr`的引用计数,主要用于避免循环引用。
3. `std::weak_ptr`不能直接访问对象,需要通过`lock`方法获取`std::shared_ptr`来访问对象。
4. `std::weak_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 show() {std::cout << "MyClass show" << std::endl;}
};void weakPtrExample() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();std::weak_ptr<MyClass> weakPtr = ptr1;if (auto sharedPtr = weakPtr.lock()) {sharedPtr->show();std::cout << "Reference count: " << sharedPtr.use_count() << std::endl;} else {std::cout << "The managed object has been deleted." << std::endl;}
}int main() {weakPtrExample();return 0;
}
补充:
`std::make_unique`和`std::make_shared`分别是C++14和C++11时期引入的工厂函数,用于创建智能指针对象。提供了一种安全、高效的方式来分配内存并初始化智能指针,从而减少我们手动使用`new`带来的错误风险。
std::make_unique
`std::make_unique`用于创建一个`std::unique_ptr`实例。
1. 防止内存泄漏:
使用`std::make_unique`可以防止在内存分配和对象构造之间发生异常时的内存泄漏问题。
2. 安全方面:
相比手动使用`new`,`std::make_unique`更加安全,不会产生裸指针。
#include <iostream>
#include <memory>class MyClass {
public:MyClass(int x) : value(x) {std::cout << "MyClass Constructor, value: " << value << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "Value: " << value << std::endl;}
private:int value;
};void uniquePtrExample() {std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(42);ptr->show();
}int main() {uniquePtrExample();return 0;
}
std::make_shared
`std::make_shared`用于创建一个`std::shared_ptr`实例。
1. 单次内存分配:
`std::make_shared`通过一次内存分配同时创建控制块和对象,从而减少了内存分配的次数,提升了性能。
2. 防止内存泄漏:
使用`std::make_shared`可以防止在内存分配和对象构造之间发生异常时的内存泄漏问题。
3. 简洁和安全:
相比手动使用`new`,`std::make_shared`更加简洁且安全,不会产生裸指针。
#include <iostream>
#include <memory>class MyClass {
public:MyClass(int x) : value(x) {std::cout << "MyClass Constructor, value: " << value << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "Value: " << value << std::endl;}
private:int value;
};void sharedPtrExample() {std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(42);ptr->show();
}int main() {sharedPtrExample();return 0;
}
其它
`std::make_shared`通过一次内存分配同时创建控制块和对象,提升了性能,但如果不需要共享所有权,可以优先选择`std::make_unique`。
管理动态内存
我们使用`std::make_shared`创建了树状结构的节点,并使用智能指针来管理节点的生命周期。通过`std::make_shared`,我们确保了节点在超出作用域时会自动释放内存,避免了内存泄漏。
#include <iostream>
#include <memory>
#include <vector>class Node {
public:Node(int value) : value(value) {std::cout << "Node Constructor, value: " << value << std::endl;}~Node() {std::cout << "Node Destructor, value: " << value << std::endl;}void addChild(std::shared_ptr<Node> child) {children.push_back(child);}void show() const {std::cout << "Node value: " << value << ", children: ";for (const auto& child : children) {std::cout << child->value << " ";}std::cout << std::endl;}
private:int value;std::vector<std::shared_ptr<Node>> children;
};void treeExample() {auto root = std::make_shared<Node>(1);auto child1 = std::make_shared<Node>(2);auto child2 = std::make_shared<Node>(3);root->addChild(child1);root->addChild(child2);root->show();child1->show();child2->show();
}int main() {treeExample();return 0;
}
总结
1. 使用`std::make_unique`和`std::make_shared`:
优先使用`std::make_unique`和`std::make_shared`来创建智能指针,避免手动使用`new`,可以提高代码的安全性和性能。
2. 避免循环引用:
在使用`std::shared_ptr`时要特别小心循环引用的问题。可以使用`std::weak_ptr`来打破循环引用。
3. 注意生命周期:
确保智能指针的生命周期管理正确,避免悬空指针和内存泄漏。
4. 考虑性能:
虽然智能指针极大地简化了内存管理,但也带来了额外的开销,如引用计数的维护。因此,在性能敏感的代码中,应慎重使用`std::shared_ptr`。