c++ 智能指针

reinterpret_cast

  • 属于比较底层的强制转换,没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝。

传统指针存在的问题

  • 资源泄漏,内存或句柄没有被释放。
int main() {
    int *pTemp = new int;
    *pTemp = 1;
    pTemp = new int; //之前int的内存已经泄漏
    delete pTemp;
}
  • 迷途指针,多个指针指向一块内存,其中有一个指针释放了这块内存。
int main() {
    int *p1 = new int;
    int *p2 = p1;
    delete p1; // p2指向的内存已经被释放
    delete p2;
    return 0;
}
  • 野指针,未初始化,指针释放后之后未置空。其值是随机的,意味着指针指向了一个地址是不确定的变量。

智能指针

  • shared_ptr。c++11引入,实现共享所有权。
  • unique_ptr。c++11引入,实现独享所有权。
  • auto_ptr。和unique_ptr 类似,c++11弃用。

共享指针shared_ptr

  • 通过强引用计数实现。
  • 具有共享所有权语义。
  • 每当shared_ptr的最后一个所有都被销毁时,关联资源将被清除。
  • 解决了资源泄漏问题。
#include <string>
#include <vector>
using namespace std;
int main() {
    // 1
    shared_ptr<string> p1 {new string("liangze")};
    // 2
    shared_ptr<string> p2;
    p2.reset(new string("liangze"));
    // 3 推荐,更快(一次复制),更安全
    shared_ptr<string> p3 = make_shared<string>("liangze");
    
    (*p1)[0] = 'L';
    cout << *p1 << endl;
    
    p2->replace(0, 1, "L");
    cout << *p2 << endl;

    vector<shared_ptr<string>> v;
    v.push_back(p1);
    v.push_back(p3);
    v.push_back(p1);
    (*p1)[1] = 'I';
    for (auto it = v.begin(); it != v.end(); it ++) {
        cout << **it << " ";
    }
    cout << endl;
    return 0;
}
  • 所有比较运算符都会调用共享指针内部封装的原始指针的比较运算符,同类型的共享指针才能使用。
int main() {
    shared_ptr<int> p1 = make_shared<int>(1);
    shared_ptr<int> p2 = make_shared<int>(2);
    shared_ptr<int> p3;
    shared_ptr<double> p4 = make_shared<double>(1);
    
    cout << (p1 > p2) << endl;
    cout << (p1 > p3) << endl;
    cout << (p3 == p3) << endl;
    cout << (p1 > p4) << endl;//编译错误
    return 0;
}
  • 共享指针允许强转
int main() {
    shared_ptr<void> p1{ new int };
    shared_ptr<int> p2 = static_pointer_cast<int>(p1);
    shared_ptr<int> p3{ static_cast<int *>(p1.get()) };
    return 0;
}
  • 共享指针不是线程安全,通过原子(不可被拆分的)接口,只针对并发访问的指针,而不是所引用的值。

  • 多个共享指针不能拥有同一个对像。可以用方法生成共享指针。

class Person: public enable_shared_from_this<Person> {
public:
    shared_ptr<Person> getSharePtr() {
        return shared_from_this();
    }
    
    ~Person() {
        cout << "~Person" << endl;
    }
};
int main() {
    Person *p = new Person;
    shared_ptr<Person> p1(p);
    shared_ptr<Person> p2 = p->getSharePtr();//运行时错误
    return 0;
}
  • 销毁。可定义删除器,可以定义为普通函数,匿名函数,函数指针等符合签名要求的可调用对像,符合引用计数销毁规则。
  • 为数组创建一个共享指针时,要使用自定义的删除器,因为共享指针提供的默认删除程序调用delete而不是delete[]
  • 要释其他资源,就必须使用自定义删除器。
void delFunc(string *p) {
    cout << "delFunc" << *p << endl;
}
int main() {
    cout << "begin" << endl;
    shared_ptr<string> p1;
    {
        shared_ptr<string> p2(new string("liangze"), [](string *p) {
            cout << "lamda delete" << *p << endl;
            delete p;
        });
        p1 = p2;
        shared_ptr<string> p3(new string("liangze2"), delFunc);
    }
    cout << "end" << endl;
    return 0;
}

/* 输出
begin
delFuncliangze2
end
lamda deleteliangze
*/

弱指针weak_ptr

  • 共享指针存在的问题:1,循环引用;2,明确想要共享但不拥有对像。
    image.png

  • 弱指针是共享指针的辅助类,允许共享但不拥有对像,困此,不会增加关联对像的引用次数。

  • 不能使用运算符*->直接访问弱指针引用的对象,而是使用lock函数生成关联对像的共享指针(可能为空)。

  • 当拥有该对象的最后一个共享指针失去所有权时,任何弱指针都会自动变为空。

int main() {
    shared_ptr<string> p1{ new string("1234") };
    cout << p1.use_count() << endl;

    weak_ptr<string> w1 = p1;
    cout << p1.use_count() << " " << *w1.lock() << endl;
    p1.reset();
    cout << p1.use_count() << endl;

    if (w1.lock() == nullptr) {
        cout << "nullptr" << endl;
    }
    return 0;
}
  • 弱指针可以解决共享指针存在的问题。
    image.png

自动指针auto_ptr

  • c++98提供,已废弃,使用unique_ptr替代
  • unique_ptr编译不过。
template <typename T>
// 形参ap获得传进来参数的所有权
void bad_print(auto_ptr<T> ap) {
    if (ap.get() == NULL) {
        cout << "NULL";
    } else {
        cout << *ap;
    }
    cout << endl;
};

int main() {
    auto_ptr<int> ap(new int);
    *ap = 42;
    *ap = 50;
    bad_print(ap); //执行完ap释放
    *ap = 60; // 运行错误,exc_bad_access
    return 0;
}

总结

  • c++11提供了智能指针的两个慨念:
    • 共享指针用于共享所有权。
    • 唯一指针用于独占所有权,取代自动指针。
  • 共享指针通过非侵入性方法实现,需要额外的性能开销,如内存。
  • 唯一指针通过特殊的构造函数和析构函数以及消除复制,不需要额外的性能开销。
  • 必须知道它们解决了哪些问题以及存在哪些问题。
  • 一般来说,智能指针不是线程安全的
  • 推荐网站:https://cplusplus.com/reference