Pointer

示例

#include <iostream>

using namespace std;

int main () {
    int i = 6;
    int *p_i = &i;
    cout << "  i : " << i << endl;
    cout << "  &i: " << &i << endl;
    cout << "p_i : " << p_i << endl;
    cout << "*p_i: " << *p_i << endl;
    return 0;
}

/** Output
  i : 6
  &i: 0x7ffeee386644
p_i : 0x7ffeee386644
*p_i: 6
*/
#include <iostream>

using namespace std;

int main () {
    char* a, b;        // a: char *; b: char
    char *c, d;        // c: char *; d: char
    cout << "sizeof(a):" << sizeof(a) << endl;
    cout << "sizeof(b):" << sizeof(b) << endl;
    cout << "sizeof(c):" << sizeof(c) << endl;
    cout << "sizeof(d):" << sizeof(d) << endl;
    return 0;
}

/** Output
sizeof(a):8
sizeof(b):1
sizeof(c):8
sizeof(d):1
*/

指针和引用

对比

  • 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。

  • 引用在定义的时候必须进行初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且指向的空间可变。

  • 有多级指针,但是没有多级引用,只能有一级引用。

  • 指针支持多种操作,比如++。

  • sizeof 引用得到的是所指向的变量(对象)的大小,而sizeof 指针得到的是指针本身的大小。

  • 引用访问一个变量是直接访问,而指针访问一个变量是间接访问。

智能指针

智能指针可以很好地解决:

  • 内存泄漏

    • 用动态存储分配函数(如malloc)动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,直到程序结束

    • 内存泄漏是指堆内存泄漏

  • 悬挂指针

    • 未初始化的指针

      • 指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的

    • 未清零的指针

      • 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针

四种智能指针

智能指针的原理都是RAII(Resource Acquisition Is Initialization),即在构造的时候获取资源,在析构的时候释放资源。

为了使用智能指针,需要引入头文件 #include <memory>

auto_ptr

c++98

解决内存泄漏问题

#include <iostream>
#include <memory>

using namespace std;

class A {
public:
    A () {}
    ~A() {
        cout << "Calling A's destructor" << endl;
    }
};

class B {
public:
    B () {}
    ~B() {
        cout << "Calling B's destructor" << endl;
    }
};

int main() {
    auto_ptr<A> a(new A());
    B *b = new B();
    return 0;
}
/**output
Calling A's destructor
*/

解决浅拷贝导致的指针悬挂问题

#include <iostream>
#include <memory>
using namespace std;

class HasPtr
{
public:
    HasPtr(char *s);
    ~HasPtr();

private:
    char *ptr;
};

HasPtr::HasPtr(char *s)
{
    if (s == nullptr)
    {
        ptr = new char[1];
        *ptr = '\0';
    }
    else
    {
        ptr = new char[strlen(s) +1];
        strcpy(ptr, s);
    }
}

HasPtr::~HasPtr()
{
    cout << "destructor ---> " << ptr << endl;
    delete ptr;
}

int main()
{
    auto_ptr<HasPtr>p1(new HasPtr("Book"));
    auto_ptr<HasPtr>p2(p1); // 所有权转移到p2,p1变为empty

    return 0;
}
/** Output
destructor ---> Book
*/

需要注意的问题

  • 所有权转移

    • auto_ptr transfers the ownership when it is assigned to another auto_ptr. This is really an issue while passing the auto_ptr between the functions. Say, I have an auto_ptr in Foo( ) and this pointer is passed another function say Fun( ) from Foo. Now once Fun( ) completes its execution, the ownership is not returned back to Foo.

  • 多个auto_ptr不能同时拥有同一个对象

  • 不能用auto_ptr管理数组指针

  • auto_ptr不可做为容器(vector, list, map)元素

unique_ptr

  • C++ 11 引入

基本操作

  • get()

    • 获得原生指针

  • reset()

    • 重置,显式释放资源

  • reset(new X)

    • 重置,重新指定对象

  • release()

    • 释放所有权到某一原生指针上

可以通过std::move将所有权由一个unique_ptr对象转移到另一个unique_ptr对象上。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    //1. unique_ptr的创建
    //1.1)创建空的,然后利用reset指定对象
    unique_ptr<int> up1; 
    up1.reset(new int(3)); 
    //1.2)通过构造函数在创建时指定动态对象
    unique_ptr<int> up2(new int(4));

    //2. 获得原生指针(Getting raw pointer )
    int* p = up1.get();

    //3.所有权的变化
    //3.1)释放所有权,执行后变为empty
    int *p1 = up1.release();
    //3.2)转移所有权,执行后变为empty
    unique_ptr<int> up3 = std::move(up2);

    //4.显式释放资源
    up3.reset();

    return 0;
}

禁止赋值和复制

unique_ptr禁止赋值和复制,“唯一”地拥有其所指对象,同一时刻只能有一个unique_ptr实例指向给定对象。也就是说模板类unique_ptr的copy构造函数以及等号(“=”)操作符是无法使用的。

#include <iostream>
#include <memory>
using namespace std;

void Fun1( unique_ptr<int> up )
{ }

int main() {
    unique_ptr<int> up1 = unique_ptr<int>(new int(10));

    //不允许复制(Copy construction is not allowed),所以以下三个均错误
    unique_ptr<int> up2 = up1;  // error
    unique_ptr<int> up3(up1);   // error
    Fun1(up1);                  // error

    //不允许赋值('='),所以下面错误
    unique_ptr<int> up4;
    up4 = up1;                  // error

    return 0;
}

管理数组指针

unique_ptr< Test[ ] > uptr1(new Test[3]);
//注意 unique_ptr<Test> uptr3(new Test[3]);是不对的
unique_ptr<int[]> uptr2(new int[5]);

做容器元素

vector<unique_ptr<int> > vec;
unique_ptr<int> ptr1(new int(3));
vec.push_back(std::move(ptr1));
//vec.push_back(ptr1); //由于禁止复制这样不行

shared_ptr

​ shared_ptr has the notion called shared ownership. The goal of shared_ptr is very simple: Multiple shared pointers can refer to a single object and when the last shared pointer goes out of scope, memory is released automatically. 从上面这段英文可以看出,shared_ptr是共享所有权的,其内部有一个计数机制。

使用

  • get():获取原生指针

  • reset():重置,显式释放资源

  • reest(new X):重新指定对象

  • unique():检测对象管理者是否只有一个shared_ptr实例

在shared_ptr的RAII实现机制中,默认使用delete实现资源释放,也可以定义自己的函数来释放,比如当其管理数组指针时,需要delete[],这时就需要自定义释放函数。自定义释放的方法有两种:lambda表达式和括号操作符的重载。

另外可以通过dynamic_pointer_cast实现继承中的转换,具体见下例。

#include <memory>
#include <iostream>
using namespace std;

class Dealloc
{
public:
    Dealloc()
    {}

    //括号()操作符的重载
    void operator() (int* p )
    {
        if (p != nullptr)
        {
            //Do the custom deallocation job
            cout << "Dealloc called to release the resource " << p 
                 << " whose value is " << *p<<endl;
            delete p;
            p = nullptr;
        }
    }
};

class Base
{
public:
    Base() {}
    // 虚函数保证Base的多态性,以便在dynamic_pointer_cast中使用
    virtual void Foo() {}
};

class Derived : public Base
{
public:
    Derived() {}
};

int main()
{
    //1. 创建
    shared_ptr<int> sp1 = shared_ptr<int>(new int(100));
    shared_ptr<int> sp2 = make_shared<int>(int(10));
    auto sp3 = shared_ptr<int>(nullptr);
    if( sp3 == nullptr )
    {
        cout<<"Null pointer" << endl;
    }

    //2. 自定义资源释放函数
    {
        // lamdba表达式
        auto sp4 = shared_ptr<int>(new int[5], [ ](int* p){
            cout<<"In lambda releasing array of objects..."<<endl;
            delete[ ] p;});
    }
    {
        // 括号()操作符的重载
        auto sp5 = shared_ptr<int>(new int(1000), Dealloc() );
    }

    //3. 复制
    auto sp6(sp1);
    auto sp7 = sp1;

    //4. Getting raw pointer
    int* pRaw = sp2.get( );

    //5. Get how many shared pointers sharing the resource
    long nCount1 = sp1.use_count();
    long nCount2 = sp2.use_count();

    //6. Is this only shared pointer sharing the resource
    bool b1 = sp1.unique();
    bool b2 = sp2.unique();

    //7. swap
    sp1.swap(sp2);

    //8. reset
    sp1.reset();
    sp1.reset(new int(20));

    //9.Using dynamic_cast_pointer on shared pointer
    auto sp10 = shared_ptr<Derived>(new Derived( ));
    shared_ptr<Base> sp11 = dynamic_pointer_cast<Base>(sp10);
    if (sp11.get( ) != nullptr )
    {
        cout << "Dynamic casting from sp10 to sp11 succeeds...." << endl;
    }

    auto sp12 = shared_ptr<Base>(new Base());
    shared_ptr<Derived> sp13 = dynamic_pointer_cast<Derived>(sp12);
    if (sp13 != nullptr)
    {
        cout << "Dynamic casting from 12 to 13 succeeds...." << endl;
    }
    else
    {
        cout << "Dynamic casting from sp12 to sp13 failed ...." << endl;
    }

    return 0;
}
  • shared_ptr对象的引用是强引用(stong_ref)

最后更新于

这有帮助吗?