C++ 智能指针

 

auto_ptr

在 C++98 开始推出 auto_ptr 管理裸指针,让开发者不需要手动释放指针内存,当生命周期结束时自动释放内存。但是 auto_ptr 在转移所有权会出现野指针导致崩溃。

root@ubuntu:~/c++# cat ptr.cpp 
#include <iostream>
#include <memory>
void autoPtr()
{
     std::auto_ptr<int> auto_ptr1(new int (1));
     std::auto_ptr<int> auto_ptr2 = auto_ptr1;  // 转移控制权
     *auto_ptr1 += 1;   // 程序崩溃
     // 由于auto_ptr1转移给auto_ptr2,auto_ptr1变为空指针,导致程序崩溃,可以通过get方法先判空
     if (auto_ptr1.get())
     { // 判空后执行
       // do somethings
     }
}
int main()
{
   autoPtr();
   return 0;
}

 

root@ubuntu:~/c++#  g++ ptr.cpp  -o obj
root@ubuntu:~/c++# ./obj
Segmentation fault

 

unique_ptr

1、如何创建unique_ptr

unique_ptr 不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。

要想创建一个 unique_ptr,我们需要将一个 new 操作符返回的指针传递给unique_ptr的构造函数。

示例:

int main() {
    // 创建一个unique_ptr实例
    unique_ptr<int> pInt(new int(5));
    cout << *pInt;
}

2、无法进行复制构造和赋值操作

unique_ptr没有 copy 构造函数,不支持普通的拷贝和赋值操作。

示例:

int main() {
    // 创建一个unique_ptr实例
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2(pInt);    // 报错
    unique_ptr<int> pInt3 = pInt;   // 报错
}

3、可以进行移动构造和移动赋值操作

unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。

如果需要转移所有权,可以使用std::move()函数。

示例:

int main() {
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权
    //cout << *pInt << endl; // 出错,pInt为空
    cout << *pInt2 << endl;
    unique_ptr<int> pInt3(std::move(pInt2));
}

4、可以返回unique_ptr

unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。

示例:

unique_ptr<int> clone(int p)
{
    unique_ptr<int> pInt(new int(p));
    return pInt;    // 返回unique_ptr
}

int main() {
    int p = 5;
    unique_ptr<int> ret = clone(p);
    cout << *ret << endl;
}

C++11 新出现unique_ptr智能指针管理内存,从命名上可以知道,该智能指针智能被唯一一个对
象持有
,与 auto_ptr 一样拥有唯一拥有控制权的特性,不同的是 unique_ptr 是没有复制构造函数的,只能通过显示std::move()方法转移控制权。

include <iostream>
#include <memory>
void uniq_ptr()
{
     std::unique_ptr<int> unique_ptr1(new int (1));
     std::unique_ptr<int> unique_ptr2= unique_ptr1;  // 转移控制权
     *unique_ptr1+= 1;   // 程序崩溃
}
int main()
{
   uniq_ptr();
   return 0;
}

 

int main() {
    // 创建一个unique_ptr实例
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2(pInt);    // 报错
    unique_ptr<int> pInt3 = pInt;   // 报错
}

 

 

root@ubuntu:~/c++#  g++ -std=c++11 -pthread ptr.cpp  -o obj
ptr.cpp: In function ‘void uniq_ptr()’:
ptr.cpp:6:40: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
      std::unique_ptr<int> unique_ptr2= unique_ptr1;  // 转移控制权
                                        ^
In file included from /usr/include/c++/5/memory:81:0,
                 from ptr.cpp:2:
/usr/include/c++/5/bits/unique_ptr.h:359:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^

 

#include <iostream>
#include <memory>
void uniq_ptr()
{
     std::unique_ptr<int> unique_ptr1(new int (1));
     std::unique_ptr<int> unique_ptr2= std::move(unique_ptr1);  // 转移控制权
     *unique_ptr1+= 1;   // 程序崩溃
}
int main()
{
   uniq_ptr();
   return 0;
}
~

 

root@ubuntu:~/c++#  g++ -std=c++11 -pthread ptr.cpp  -o obj
root@ubuntu:~/c++# ./obj 
Segmentation fault
root@ubuntu:~/c++#

 

#include <memory>
#include <iostream>

class A {
public:
    A() {}

    ~A() {}
};

class B {
public:
    B() : a(new A()) {

    }

    void output() {
        std::cout << "这是B对象" << std::endl;
    };
    void test() {
        std::cout << "this B  test " << std::endl;
    };

    ~B() {
        // 不释放a
        std::cout << "destruction B " << std::endl;
    }

private:
    A *a;
};

int main(int argc, char *argv[]) {
    // B析构函数中A对象不会释放,导致内存泄露
    B *b = new B();
    b->test();

    // 使用unique_ptr会自动释放内存
    auto unique_ptr1 = std::unique_ptr<B>(new B());
    unique_ptr1->output();

    return 0;
}

 

root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
root@ubuntu:~/c++# ./obj
this B  test 
这是B对象
destruction B

 

只调用一次析构

 

shared_ptr

相对比unique_ptrshared_ptr支持复制,即多个对象共享同一块内存。采用引用计数的方式式实现内存的自动管理,当复制一次值 + 1,释放一次则值 - 1,直到值为 0 内存会被回收。

 

#include <iostream>
#include <memory>

using namespace std;

int main() {

    // 推荐方式
    shared_ptr<int> sharedPtr1(new int(5)); // 匿名函数
    shared_ptr<int> sharedPtr2 = make_shared<int>(5); // 工厂模式

    // 引用计数
    std::shared_ptr<int> sharedPtr3 = sharedPtr1;
    cout << sharedPtr1.use_count() << std::endl;  // 输出为2
    sharedPtr3.reset();                           // 释放
    cout << sharedPtr1.use_count() << endl;       // 输出为1

    return 0;
}

引用计数的方式引起循环引用导致无法被正常的释放,如下示例。

#include <iostream>
#include <memory>

using namespace std;

class Child;

class Parent {
public:
    shared_ptr<Child> child;
};

class Child {
public:
    shared_ptr<Parent> parent;
};

int main() {

    shared_ptr<Parent> parentPtr = make_shared<Parent>();
    shared_ptr<Child> childPtr = make_shared<Child>();
    // 循环引用 导致内存永远不会被释放内存泄露
    parentPtr->child = childPtr;
    childPtr->parent = parentPtr;

    return 0;
}

,shared_ptr 也支持移动

#include <iostream>
#include <memory>
void share_ptr()
{
     std::shared_ptr<int> shared_ptr1(new int (1));
     std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1);  // 转移控制权
     *shared_ptr1+= 1;   // 程序崩溃
}
int main()
{
   share_ptr();
   return 0;
}

 

root@ubuntu:~/c++#  g++ -std=c++11 share.cpp  -o obj
root@ubuntu:~/c++# ./obj
Segmentation fault
root@ubuntu:~/c++# 

 

#include <iostream>
#include <memory>
void share_ptr()
{
     std::shared_ptr<int> shared_ptr1(new int (1));
     std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1);  // 转移控制权
     *shared_ptr2+= 1;   // 程序正常
}
int main()
{
   share_ptr();
   return 0;
}

 

root@ubuntu:~/c++#  g++ -std=c++11 share.cpp  -o obj
root@ubuntu:~/c++# ./obj
root@ubuntu:~/c++# 

 

weak_ptr

在上一 part 中介绍得share_ptr由于采用引用计数的方式,在循环引用时候时候 use_count 的值 + 1,导致释放后计数值依然为 1。而weak_ptr则用于解决此问题,share_ptr赋值给weak_ptr,不会引起引用计数值 + 1,只是在弱引用计数值 + 1,因此不会影响到share_ptr的生命周期。

#include <memory>
#include <iostream>

class A {
public:
    A() {}

    ~A() {}
};

class B {
public:
    B() : a(new A()) {

    }

    void output() {
        std::cout << "这是B对象" << std::endl;
    };
    void test() {
        std::cout << "this B  test " << std::endl;
    };

    ~B() {
        // 不释放a
        std::cout << "destruction B " << std::endl;
    }

private:
    A *a;
};

int main(int argc, char *argv[]) {
    B *b = new B();
    b->test();

    auto weak_ptr1 = std::weak_ptr<B>(new B());
    weak_ptr1->output();

    return 0;
}

 

obj.cpp: In function ‘int main(int, char**)’:
obj.cpp:37:46: error: no matching function for call to ‘std::weak_ptr<B>::weak_ptr(B*)’
     auto weak_ptr1 = std::weak_ptr<B>(new B());
                                              ^
In file included from /usr/include/c++/5/memory:82:0,
                 from obj.cpp:1:
/usr/include/c++/5/bits/shared_ptr.h:492:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp1>&&)
  weak_ptr(weak_ptr<_Tp1>&& __r) noexcept
  ^
/usr/include/c++/5/bits/shared_ptr.h:492:2: note:   template argument deduction/substitution failed:
obj.cpp:37:46: note:   mismatched types ‘std::weak_ptr<_Tp>’ and ‘B*’
     auto weak_ptr1 = std::weak_ptr<B>(new B());
                                              ^
In file included from /usr/include/c++/5/memory:82:0,
                 from obj.cpp:1:
/usr/include/c++/5/bits/shared_ptr.h:489:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp>&&) [with _Tp = B]
       weak_ptr(weak_ptr&&) noexcept = default;
       ^
/usr/include/c++/5/bits/shared_ptr.h:489:7: note:   no known conversion for argument 1 from ‘B*’ to ‘std::weak_ptr<B>&&/usr/include/c++/5/bits/shared_ptr.h:486:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp1>&)
  weak_ptr(const weak_ptr<_Tp1>& __r) noexcept
  ^
/usr/include/c++/5/bits/shared_ptr.h:486:2: note:   template argument deduction/substitution failed:
obj.cpp:37:46: note:   mismatched types ‘const std::weak_ptr<_Tp>’ and ‘B*’
     auto weak_ptr1 = std::weak_ptr<B>(new B());
                                              ^
In file included from /usr/include/c++/5/memory:82:0,
                 from obj.cpp:1:
/usr/include/c++/5/bits/shared_ptr.h:483:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp>&) [with _Tp = B]
       weak_ptr(const weak_ptr&) noexcept = default;
       ^
/usr/include/c++/5/bits/shared_ptr.h:483:7: note:   no known conversion for argument 1 from ‘B*’ to ‘const std::weak_ptr<B>&/usr/include/c++/5/bits/shared_ptr.h:480:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::shared_ptr<_Tp1>&)
  weak_ptr(const shared_ptr<_Tp1>& __r) noexcept
  ^
/usr/include/c++/5/bits/shared_ptr.h:480:2: note:   template argument deduction/substitution failed:
obj.cpp:37:46: note:   mismatched types ‘const std::shared_ptr<_Tp1>’ and ‘B*’
     auto weak_ptr1 = std::weak_ptr<B>(new B());
                                              ^
In file included from /usr/include/c++/5/memory:82:0,
                 from obj.cpp:1:
/usr/include/c++/5/bits/shared_ptr.h:477:17: note: candidate: constexpr std::weak_ptr<_Tp>::weak_ptr() [with _Tp = B]
       constexpr weak_ptr() noexcept = default;
                 ^
/usr/include/c++/5/bits/shared_ptr.h:477:17: note:   candidate expects 0 arguments, 1 provided

share_ptr给weak_ptr赋值

#include <memory>
#include <iostream>

class A {
public:
    A() {}

    ~A() {}
};

class B {
public:
    B() : a(new A()) {

    }

    void output() {
        std::cout << "这是B对象" << std::endl;
    };
    void test() {
        std::cout << "this B  test " << std::endl;
    };

    ~B() {
        // 不释放a
        std::cout << "destruction B " << std::endl;
    }

private:
    A *a;
};

int main(int argc, char *argv[]) {
    B *b = new B();
    b->test();

    auto weak_ptr1 =  std::make_shared<B>();
    weak_ptr1->output();

    return 0;
}

 

root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
root@ubuntu:~/c++# ./obj 
this B  test 
这是B对象
destruction B

 

int main(int argc, char *argv[]) {
    std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
    std::weak_ptr<B> weak_ptr1 = share_ptr1;
    weak_ptr1->output();

    return 0;
}
~

 

root@ubuntu:~/c++#  g++ -std=c++11 obj.cpp  -o obj
obj.cpp: In function ‘int main(int, char**)’:
obj.cpp:36:14: error: base operand of ‘->’ has non-pointer type ‘std::weak_ptr<B>’
     weak_ptr1->output();
              ^
root@ubuntu:~/c++# 

 

int main(int argc, char *argv[]) {
    std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
    std::weak_ptr<B> weak_ptr1 = share_ptr1;
    weak_ptr1.lock()->output();

    return 0;
}

 

 

root@ubuntu:~/c++# ./obj
这是B对象
destruction B 
root@ubuntu:~/c++# 

 

 

int weak(std::shared_ptr<B> & sh) {
    std::weak_ptr<B> weak_ptr1 = sh;
    weak_ptr1.lock()->output();
    return 0;
}
int main(int argc, char *argv[]) {
    std::shared_ptr<B>  share_ptr1 = std::make_shared<B>();
    weak(share_ptr1);
    return 0;
}

 

root@ubuntu:~/c++# ./obj
这是B对象
destruction B 
root@ubuntu:~/c++# 

 weak析构时,不调用对象的析构函数

 

#include <iostream>
#include <memory>

using namespace std;

class Child;

class Parent {
public:
    shared_ptr<Child> child;
};

class Child {
public:
    weak_ptr<Parent> parent;  // 改为weak_ptr不会导致强引用计数值+1
};

int main() {

    shared_ptr<Parent> parentPtr = make_shared<Parent>();
    shared_ptr<Child> childPtr = make_shared<Child>();
    // 循环引用 导致内存永远不会被释放内存泄露
    parentPtr->child = childPtr;
    childPtr->parent = parentPtr;

    return 0;
}

 

 

接下来,我们来看看weak_ptr的简单用法。

 如何创建weak_ptr实例

当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。

示例:

int main() {
    shared_ptr<int> sp(new int(5));
    cout << "创建前sp的引用计数:" << sp.use_count() << endl;    // use_count = 1

    weak_ptr<int> wp(sp);
    cout << "创建后sp的引用计数:" << sp.use_count() << endl;    // use_count = 1
}
 

如何判断weak_ptr指向对象是否存在
既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,我们就不能使用weak_ptr直接访问对象。那么我们如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr。
————————————————
 

class A
{
public:
    A() : a(3) { cout << "A Constructor..." << endl; }
    ~A() { cout << "A Destructor..." << endl; }

    int a;
};

int main() {
    shared_ptr<A> sp(new A());
    weak_ptr<A> wp(sp);
    //sp.reset();

    if (shared_ptr<A> pa = wp.lock())
    {
        cout << pa->a << endl;
    }
    else
    {
        cout << "wp指向对象为空" << endl;
    }
}
 

 

试试把sp.reset()这行的注释去掉看看结果有什么不同。

除此之外,weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。

示例:

 

class A
{
public:
    A() : a(3) { cout << "A Constructor..." << endl; }
    ~A() { cout << "A Destructor..." << endl; }

    int a;
};

int main() {
    shared_ptr<A> sp(new A());
    weak_ptr<A> wp(sp);
    sp.reset(); // 此时sp被销毁
    cout << wp.expired() << endl;  // true表示已被销毁,否则为false
}
 

 

A Constructor...
A Destructor...
1

如何使用weak_ptr
weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

最后,我们来看看如何使用weak_ptr来改造最前面的代码,打破循环引用问题。
 

class ClassB;

class ClassA
{
public:
    ClassA() { cout << "ClassA Constructor..." << endl; }
    ~ClassA() { cout << "ClassA Destructor..." << endl; }
    weak_ptr<ClassB> pb;  // 在A中引用B
};

class ClassB
{
public:
    ClassB() { cout << "ClassB Constructor..." << endl; }
    ~ClassB() { cout << "ClassB Destructor..." << endl; }
    weak_ptr<ClassA> pa;  // 在B中引用A
};

int main() {
    shared_ptr<ClassA> spa = make_shared<ClassA>();
    shared_ptr<ClassB> spb = make_shared<ClassB>();
    spa->pb = spb;
    spb->pa = spa;
    // 函数结束,思考一下:spa和spb会释放资源么?
}
 

 

ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0

 

 

 

参考文章
C++ 智能指针的正确使用方式
C++ 知识点 | C11 智能指针

posted on 2021-03-30 16:08  tycoon3  阅读(130)  评论(0编辑  收藏  举报

导航