C++学习笔记——(十)智能指针,类型转换

注:编码工具是CLion+Cygwin64

目录

智能指针

shared_ptr

引用计数器

循环引用

weak_ptr

unique_ptr

仿写智能指针

类型转换

const_cast

static_cast

dynamic_cast

reinterpret_cast


智能指针

        智能指针可以用于自动回收new方式创建的对象。使用前需要先导入memory头文件。

shared_ptr

#include <iostream>

using namespace std;

#include <memory>
class Test{
public:
    ~Test(){
        cout << "Test析构函数" << endl;
    }
};
int main(){
    Test * test = new Test;
    shared_ptr<Test> ptr(test);
    return 0;
}

输出:

Test析构函数

        可以看到上面代码中没有调用delete函数回收对象,对象的析构函数也被调用了。       

引用计数器

        智能指针内部会有一个对象引用计数器,记录对象被智能指针引用的次数。

#include <iostream>

using namespace std;

#include <memory>
class Test{
public:
    ~Test(){
        cout << "Test析构函数" << endl;
    }
};
int main(){
    Test * test = new Test;
    shared_ptr<Test> ptr(test);
    cout << "引用计数值为:" << ptr.use_count() << endl;
    shared_ptr<Test> ptr2 = ptr;
    cout << "引用计数值为:" << ptr.use_count() << endl;
    cout << "引用计数值为:" << ptr2.use_count() << endl;
    return 0;
}

输出:

引用计数值为:1
引用计数值为:2
引用计数值为:2
Test析构函数

循环引用

        shared_ptr类型的智能指针可能导致循环引用,因而对象的析构函数无法被调用。

#include <iostream>

using namespace std;

#include <memory>
class B;
class A{
public:
    shared_ptr<B> ptr;

    ~A(){
        cout << "A析构函数" << endl;
    }
};

class B{
public:
    shared_ptr<A> ptr;

    ~B(){
        cout << "B析构函数" << endl;
    }
};


int main(){
    A * a = new A;
    B * b = new B;
    shared_ptr<A> ptrA(a);
    shared_ptr<B> ptrB(b);
    a->ptr = ptrB;
    b->ptr = ptrA;

    cout << "A的引用计数为:" << ptrA.use_count() << endl;
    cout << "B的引用计数为:" << ptrB.use_count() << endl;
    return 0;
}

输出:

A的引用计数为:2
B的引用计数为:2

        可以看到并没有输出A和B的析构函数中的打印信息,所以A和B的析构函数没有被调用。这是因为A和B都是通过new方式创建的对象,虽然ptrA和ptrB在main函数执行完被回收了,引用计数器也都减了1,但是A和B中的相互引用还存在,引用计数不为0,智能指针中没有调用delete函数释放A和B。

        ·要解决循环引用的问题,可以用另一种智能指针weak_ptr。

weak_ptr

        weak_ptr类型的智能指针,不会增加对象的引用计数。

#include <iostream>

using namespace std;

#include <memory>
class B;
class A{
public:
    weak_ptr<B> ptr;

    ~A(){
        cout << "A析构函数" << endl;
    }
};

class B{
public:
    weak_ptr<A> ptr;

    ~B(){
        cout << "B析构函数" << endl;
    }
};


int main(){
    A * a = new A;
    B * b = new B;
    shared_ptr<A> ptrA(a);
    shared_ptr<B> ptrB(b);
    a->ptr = ptrB;
    b->ptr = ptrA;

    cout << "A的引用计数为:" << ptrA.use_count() << endl;
    cout << "B的引用计数为:" << ptrB.use_count() << endl;
    return 0;
}

输出:

A的引用计数为:1
B的引用计数为:1
B析构函数
A析构函数

unique_ptr

        unique_ptr不允许同类型的类型指针赋值。

        unique_ptr没有引用计数。

#include <iostream>

using namespace std;

#include <memory>

class Test{
public:
    ~Test(){
        cout << "Test析构函数" << endl;
    }
};

int main(){
    Test * test = new Test;
    unique_ptr<Test> ptr(test);
//    unique_ptr<Test> ptr2 = ptr; // 编译不通过
    return 0;
}

输出:

Test析构函数

仿写智能指针

#include <iostream>

using namespace std;

template <typename T>
class Ptr{
private:
    T * object;
    int * count;
public:
    Ptr(){
        object = NULL;
        count = new int(1);
    }

    Ptr(T * t){
        object = t;
        count = new int(1);
    }

    ~Ptr(){
        if(--(*count) == 0){
            if(object)
            {
                delete object;
            }
            delete count;
            object = NULL;
            count = 0;
        }
    }

    // 用一个智能指针对象初始化一个新的智能指针对象时,会调用拷贝构造函数
    Ptr(const Ptr<T> & ptr){
        cout << "调用了Ptr的拷贝构造函数" << endl;
        ++(*(ptr.count));
        // 如果智能指针之前引用其他对象,则需要先释放
        if(--(*count) == 0){
            if(object){
                delete object;
            }
            delete count;
        }
        object = ptr.object;
        count = ptr.count;
    }

    // 先用默认构造函数初始化一个对象,再将另一个对象赋值给此对象时,会用到=
    // 所以要重载=运算符
    Ptr & operator=(const Ptr & ptr){
        cout << "调用了重载=运算符函数" << endl;
        ++(*(ptr.count));
        // 如果智能指针之前引用其他对象,则需要先释放
        if(--(*count) == 0){
            if(object){
                delete object;
            }
            delete count;
        }
        object = ptr.object;
        count = ptr.count;
        return *this;
    }

    int use_count(){
        return *(this->count);
    }
};

class Test{
public:
    ~Test(){
        cout << "调用了Test析构函数" << endl;
    }
};

int main(){
    Test * test = new Test();
    Ptr<Test> ptr(test);
    cout << "ptr引用计数为:" << ptr.use_count() << endl;
    // 调用拷贝构造函数
    Ptr<Test> ptr2 = ptr;
    cout << endl << "ptr引用计数为:" << ptr.use_count() << endl;
    cout << "ptr2引用计数为:" << ptr2.use_count() << endl;
    // 调用重载=运算符函数
    Ptr<Test> ptr3;
    ptr3 = ptr;
    cout << endl << "ptr引用计数为:" << ptr.use_count() << endl;
    cout << "ptr2引用计数为:" << ptr2.use_count() << endl;
    cout << "ptr3引用计数为:" << ptr3.use_count() << endl;
    return 0;
}

输出:

ptr引用计数为:1
调用了Ptr的拷贝构造函数

ptr引用计数为:2
ptr2引用计数为:2
调用了重载=运算符函数

ptr引用计数为:3
ptr2引用计数为:3
ptr3引用计数为:3
调用了Test析构函数

类型转换

const_cast

        常量转换,可以将常量指针转换为普通指针,从而可以修改该指针所指向的内存地址的值。

#include <iostream>

using namespace std;

class Test{
public:
    string name = "default";
};

int main(){
    const Test* test = new Test;
    cout << "before:name = " << test->name << endl;
//    test->name = "update";// 编译不通过
    Test * ntest = const_cast<Test*>(test);
    ntest->name = "update";
    cout << "after:name = " << test->name << endl;
    if(test){
        delete test;
        test = NULL;
    }
    return 0;
}

输出:

before:name = default
after:name = update

static_cast

        静态转换主要是转换指针类型,例如可以将void*转换为int*,double*等,前面线程中就用到了。

        静态转换还可以将父类型的对象转换为子类型。

        静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。

#include <iostream>

using namespace std;

class Base{
public:
    void show(){
        cout << "Base show" << endl;
    }
};

class Sub: public Base{
public:
    void show(){
        cout << "Sub show" << endl;
    }
};

int main(){
    int number = 9999;
    void * vp = &number;
    int * ip = static_cast<int*>(vp);
    cout << "*ip = " << *ip << endl;

    Base * base = new Base;
    // 静态转换还可以将父类型的对象转换为子类型。
    Sub * sub = static_cast<Sub*>(base);
    base->show();
    // 静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。
    sub->show();
    if(base){
        // 谁是new出来的,就delete谁
        delete base;
        base = NULL;
    }
    return 0;
}

输出:

*ip = 9999
Base show
Sub show

dynamic_cast

        动态转换运行期才能知道调用哪个函数。

        动态转换不能将父类型对象转换为子类型对象。

        动态转换返回如果是NULL,表示转换失败。

        动态转换中,父类的函数必须声明为虚函数。

#include <iostream>

using namespace std;

class Base{
public:
    virtual void show(){
        cout << "Base show" << endl;
    }
};

class Sub: public Base{
public:
    void show(){
        cout << "Sub show" << endl;
    }
};

int main(){
    Base * base = new Base;
    // 动态转换不可以将父类型的对象转换为子类型。
    Sub * sub = dynamic_cast<Sub*>(base);
    if(sub){
        cout << "Base->Sub转换成功" << endl;
        sub->show();
    }else{
        cout << "Base->Sub转换失败" << endl;
    }

    Sub * sub2 = new Sub;
    Base * base2 = dynamic_cast<Base*>(sub2);
    if(base2)
    {
        cout << "Sub->Base转换成功" << endl;
        base2->show();
    }else{
        cout << "Sub->Base转换失败" << endl;
    }

    if(base){
        // 谁是new出来的,就delete谁
        delete base;
        base = NULL;
    }
    if(sub2){
        // 谁是new出来的,就delete谁
        delete base;
        base = NULL;
    }
    return 0;
}

输出:

Base->Sub转换失败
Sub->Base转换成功
Sub show

reinterpret_cast

        强制转换,静态转换有的功能它都有,同时它还可以将对象转换为数值,可以将数值转换为对象。

        可以用于在Java和C++之间传递对象地址,Android源码中Handler和Binder都用到了reinterpret_cast,C++将对象转换为long型值传给Java,C++在需要对象的时候,Java再将long型值传给C++。

#include <iostream>

using namespace std;

class Test{
public:
    void show(){
        cout << "test reinterpret_cast" << endl;
    }
};
int main(){
    Test * test = new Test;
    long paddr = reinterpret_cast<long>(test);
    cout << "paddr = " << paddr << endl;
    Test * reTest = reinterpret_cast<Test*>(paddr);
    reTest->show();
    printf("paddr = %p\n", paddr);
    printf("test存储的地址值是:%p\n", test);
    printf("reTest存储的地址值是:%p\n", reTest);

    if(test){
        delete test;
        test = NULL;
    }
    return 0;
}

输出:

paddr = 34360045024
test reinterpret_cast
paddr = 0x80004ade0
test存储的地址值是:0x80004ade0
reTest存储的地址值是:0x80004ade0

上一篇:C语言-初阶笔记


下一篇:汇编语言3