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

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

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

注:编码工具是CLion+Cygwin64

目录

智能指针

shared_ptr

引用计数器

循环引用

weak_ptr

unique_ptr

仿写智能指针

类型转换

const_cast

static_cast

dynamic_cast

reinterpret_cast


智能指针

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

shared_ptr
#include 

using namespace std;

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

输出:

Test析构函数

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

引用计数器

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

#include 

using namespace std;

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

输出:

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

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

#include 

using namespace std;

#include 
class B;
class A{
public:
    shared_ptr ptr;

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

class B{
public:
    shared_ptr ptr;

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


int main(){
    A * a = new A;
    B * b = new B;
    shared_ptr ptrA(a);
    shared_ptr 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 

using namespace std;

#include 
class B;
class A{
public:
    weak_ptr ptr;

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

class B{
public:
    weak_ptr ptr;

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


int main(){
    A * a = new A;
    B * b = new B;
    shared_ptr ptrA(a);
    shared_ptr 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 

using namespace std;

#include 

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

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

输出:

Test析构函数
仿写智能指针
#include 

using namespace std;

template 
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 & 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 ptr(test);
    cout << "ptr引用计数为:" << ptr.use_count() << endl;
    // 调用拷贝构造函数
    Ptr ptr2 = ptr;
    cout << endl << "ptr引用计数为:" << ptr.use_count() << endl;
    cout << "ptr2引用计数为:" << ptr2.use_count() << endl;
    // 调用重载=运算符函数
    Ptr 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 

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);
    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 

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(vp);
    cout << "*ip = " << *ip << endl;

    base * base = new base;
    // 静态转换还可以将父类型的对象转换为子类型。
    Sub * sub = static_cast(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 

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(base);
    if(sub){
        cout << "base->Sub转换成功" << endl;
        sub->show();
    }else{
        cout << "base->Sub转换失败" << endl;
    }

    Sub * sub2 = new Sub;
    base * base2 = dynamic_cast(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 

using namespace std;

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

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

输出:

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5635480.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存