在之前参加项目时,有一条准则为不使用原生态指针,而使用智能指针。那么我将在本文中介绍shard_ptr的内容。本文分两个部分组成,第一部分是讲解shard_ptr的使用方法,(至于智能指针的理解,此处不赘述)以及第二部分使用shared_ptr实现链表的部分功能,(其他功能也是类似的而已)。
(知识点摘自cplusplus)
构造函数有4个参数分别有以下含义:
#include
#include
#include
using namespace std;
/*
default (1)
constexpr shared_ptr() noexcept;
from null pointer (2)
constexpr shared_ptr(nullptr_t) : shared_ptr() {}
from pointer (3)
template explicit shared_ptr (U* p);
with deleter (4)
template shared_ptr (U* p, D del);
template shared_ptr (nullptr_t p, D del);
with allocator (5)
template shared_ptr (U* p, D del, Alloc alloc);
template shared_ptr (nullptr_t p, D del, Alloc alloc);
copy (6)
shared_ptr (const shared_ptr& x) noexcept;
template shared_ptr (const shared_ptr& x) noexcept;
copy from weak (7)
template explicit shared_ptr (const weak_ptr& x);
move (8)
shared_ptr (shared_ptr&& x) noexcept;
template shared_ptr (shared_ptr&& x) noexcept;
move from managed (9)
template shared_ptr (auto_ptr&& x);
template shared_ptr (unique_ptr&& x);
aliasing (10)
template shared_ptr (const shared_ptr& x, element_type* p)
noexcept;
*/
struct C {
int* data;
};
int main() {
std::shared_ptr<int> p1;
std::shared_ptr<int> p2(nullptr);
std::shared_ptr<int> p3(new int);
std::shared_ptr<int> p4(new int, std::default_delete<int>());
std::shared_ptr<int> p5(new int, [](int* p) { delete p; },
std::allocator<int>());
std::shared_ptr<int> p6(p5);
std::shared_ptr<int> p7(std::move(p6));
std::shared_ptr<int> p8(std::unique_ptr<int>(new int));
std::shared_ptr obj(new C);
std::shared_ptr<int> p9(obj, obj->data);
std::cout << "use_count:\n";
std::cout << "p1: " << p1.use_count() << '\n';
std::cout << "p2: " << p2.use_count() << '\n';
std::cout << "p3: " << p3.use_count() << '\n';
std::cout << "p4: " << p4.use_count() << '\n';
std::cout << "p5: " << p5.use_count() << '\n';
std::cout << "p6: " << p6.use_count() << '\n';
std::cout << "p7: " << p7.use_count() << '\n';
std::cout << "p8: " << p8.use_count() << '\n';
std::cout << "p9: " << p9.use_count() << '\n';
return 0;
}
make_shared
和move
两个函数可以用于operator=操作。
/*
copy (1)
shared_ptr& operator= (const shared_ptr& x) noexcept;
template shared_ptr& operator= (const shared_ptr& x) noexcept;
move (2)
shared_ptr& operator= (shared_ptr&& x) noexcept;
template shared_ptr& operator= (shared_ptr&& x) noexcept;
move from (3)
template shared_ptr& operator= (auto_ptr&& x);
template shared_ptr& operator= (unique_ptr&& x);
*/
#include
#include
int main() {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar(new int(10));
foo = bar; // copy
bar = std::make_shared<int>(20); // move
std::unique_ptr<int> unique(new int(30));
foo = std::move(unique); // move from unique_ptr
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
return 0;
}
回收内存并且改变内存。
/*
void reset() noexcept;
template void reset (U* p);
template void reset (U* p, D del);
template void reset (U* p, D del, Alloc alloc);
*/
#include
#include
int main() {
std::shared_ptr<int> sp; // empty
sp.reset(new int); // takes ownership of pointer
*sp = 10;
std::cout << *sp << '\n';
sp.reset(new int); // deletes managed object, acquires new pointer
*sp = 20;
std::cout << *sp << '\n';
sp.reset(); // deletes managed object
return 0;
}
构造内存。
/*
template
shared_ptr make_shared (Args&&... args);
*/
#include
#include
int main() {
std::shared_ptr<int> foo = std::make_shared<int>(10);
// same as:
std::shared_ptr<int> foo2(new int(10));
auto bar = std::make_shared<int>(20);
auto baz = std::make_shared<std::pair<int, int>>(30, 40);
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';
return 0;
}
/*
template
shared_ptr allocate_shared (const Alloc& alloc, Args&&... args);
*/
#include
#include
int main () {
std::allocator<int> alloc; // the default allocator for int
std::default_delete<int> del; // the default deleter for int
std::shared_ptr<int> foo = std::allocate_shared<int> (alloc,10);
auto bar = std::allocate_shared<int> (alloc,20);
auto baz = std::allocate_shared<std::pair<int,int>> (alloc,30,40);
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';
return 0;
}
#include
#include
using namespace std;
template <typename T>
struct node {
T value;
shared_ptr next;
node(T val = 0, shared_ptr n = nullptr) : value(val), next(n) {}
};
template <typename T>
class list {
private:
typedef shared_ptr> pointer;
typedef T value_type;
private:
pointer head;
size_t size;
public:
list() : head(make_shared>()), size(0) {}
~list() {}
void push_back(const value_type& val) {
pointer temp = head;
if (temp.get()->next != nullptr) {
temp = temp.get()->next;
}
auto temps = make_shared>(val);
(*temp.get()).next = temps;
size++;
}
void push_front(const value_type& val) {
pointer temp = make_shared>(0, head);
head.get()->value = val;
head = temp;
size++;
}
bool insert(int pos, const value_type& val) {
if (pos == 0) {
push_front(val);
return true;
}
if (pos == size - 1) {
push_back(val);
return true;
}
if (pos > size) {
return false;
} else {
pointer temp = head.get()->next;
for (int i = 0; i != pos; i++) {
temp = temp.get()->next;
}
pointer add = make_shared>(val, nullptr);
temp.get()->next = add;
size++;
return true;
}
}
bool erase(int pos) {
if (pos == 0) {
head = head.get()->next;
size--;
return true;
}
if (pos >= size) {
return false;
} else {
pointer del = head.get()->next;
for (int i = 0; i != pos - 1; i++) {
del = del.get()->next;
}
del.get()->next = del.get()->next->next;
size--;
return true;
}
}
friend ostream& operator<<(ostream& out, list & orig) {
pointer temp = orig.head.get()->next;
while (temp.get()->next != nullptr) {
out << temp.get()->value << " ";
temp = temp.get()->next;
}
out << temp.get()->value << endl;
return out;
}
};
int main() {
list<int> li;
li.push_back(10);
li.push_back(123);
li.push_front(12);
li.insert(0, 20);
li.erase(2);
cout << li;
return 0;
}
这个shared_ptr
的链表实现版本比我想象中要出现更多的问题,最主要的问题是我夸大了shared_ptr
的内存管理能力。一开始我以为shared_ptr
的对象只要到达使用析构函数的部分,不管他的引用计数为多少都会被回收资源(因为他们不会再被使用)。但实际情况并不是如此,并没有这么智能!到达析构函数时,如果引用计数大于1,那么他们只会调用-1的操作,并不会直接忽略引用计数而直接析构资源。(细想一下,确实应当如此。)基于最初的妄想,我设计了一个双头链表(有next
和prev
对象),最后出现了大量的内存泄露。最后我把node设计为单向链表,然后就没有内存泄露了。
虽然使用智能指针仍然要考虑引用计数的问题,但还是应该承认使用智能指针确实比使用原生态的指针方便太多,特别是在erase元素的时候。