C++ 指针特别篇-指针转换和智能指针

智能指针

智能指针的头文件,#include <memory>

① shared_ptr

操作引用计数实现共享式拥有的概念。多个智能指针可以指向相同的对象,这个对象和其相关资源会在最后一个被销毁时释放。

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

class Person{
public:
    virtual ~Person() {
        cout << " ~Person 析构" << endl;
    }
};

int main(){
    Person * person1 = new Person();
//    delete person1;
    shared_ptr<Person> shared_ptr(person1);
    return 0;
};

一些有经验的多年C++程序不用,有潜在的bug。特别简单的逻辑可以用。

再看一个例子


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

class Person{
public:
    virtual ~Person() {
        cout << " ~Person 析构" << endl;
    }
};

int main(){
    Person * person1 = new Person();
    Person * person2 = new Person();
//TODO 1------------------
//    delete person1;
//    shared_ptr<Person> shared_ptr(person1);
// ----------------- END



//TODO 1------------------
    // person1栈开销,引用计数+1。
    shared_ptr<Person> shared_ptr1(person1);
    shared_ptr<Person> shared_ptr2(person2);
// ----------------- END

    return 0;
};//函数弹栈 persont1 析构函数 -1 等于0 释放 person1

TODO1 部分的代码,delete person 和 shared_ptr 效果一样,最终都会调用析构函数,这样我们在使用就可以避免忘记写销毁代码delete person

shared_ptr<Person> shared_ptr1(person1);
内部原理是 :
person1栈开销,引用计数+1。
main函数弹栈 persont1 析构函数 -1 等于0 释放 person1

循环依赖问题《错误》演示

// 演示只能指针循环依赖

#include <iostream>
#include <memory>
using namespace std;
class Person2;
class Person1{
public:
    shared_ptr<Person2> person2;
    virtual ~Person1() {
        cout << " ~Person 析构" << endl;
    }
};


class Person2{

public:
    virtual ~Person2() {
        cout << " ~Person 析构" << endl;

    }

    shared_ptr<Person1> person1;
};
int main(){
    Person1 * person1 = new Person1();
    Person2 * person2 = new Person2();

    shared_ptr<Person1> shared_ptr1(person1);
    shared_ptr<Person2> shared_ptr2(person2);

    cout << " shared_ptr1 引用计数" << shared_ptr1.use_count() << endl;
    cout << " shared_ptr2 引用计数" << shared_ptr2.use_count() << endl;

    // 给person2 赋值
    person1->person2 = shared_ptr2;
    person2->person1 = shared_ptr1;
    cout << " shared_ptr1 引用计数" << shared_ptr1.use_count() << endl;
    cout << " shared_ptr2 引用计数" << shared_ptr2.use_count() << endl;
    return 0;
}

② weak_ptr 解决循环依赖

weak_ptr是为配合shared_ptr而引入的一种智能指针。主要用于观测资源的引用情况。
它的构造和析构不会引起引用记数的增加或减少。没有重载*和->但可以使用lock获得一个可用的shared_ptr对象。

配合shared_ptr解决循环引用问题

#include <iostream>
#include <memory>
using namespace std;
class Person2;
class Person1{
public:
    weak_ptr<Person2> person2;
    virtual ~Person1() {
        cout << " ~Person 析构" << endl;
    }
};


class Person2{

public:
    virtual ~Person2() {
        cout << " ~Person 析构" << endl;

    }

    weak_ptr<Person1> person1;
};
int main(){
    Person1 * person1 = new Person1();
    Person2 * person2 = new Person2();

    shared_ptr<Person1> shared_ptr1(person1);
    shared_ptr<Person2> shared_ptr2(person2);

    cout << " shared_ptr1 引用计数" << shared_ptr1.use_count() << endl;
    cout << " shared_ptr2 引用计数" << shared_ptr2.use_count() << endl;

    // 给person2 赋值
    person1->person2 = shared_ptr2;
    person2->person1 = shared_ptr1;
    cout << " shared_ptr1 引用计数" << shared_ptr1.use_count() << endl;
    cout << " shared_ptr2 引用计数" << shared_ptr2.use_count() << endl;
    return 0;
}

将 声明的shared_ptr 改成weak_ptr

weak_ptr 提供expired 方法等价于 use_count == 0,当expired为true时,lock返回一个存储空指针的shared_ptr

③ unique_ptr

实现独占式引用,保证同一时间只有一个智能指针指向内部对象。
unique_ptr<A> a(new A());

auto_ptr已经不推荐使用

手写智能指针

只能指针可能有三种赋值的方式如下

#include <iostream>
#include <memory>

using namespace std;

class Person {
};

int main() {
    Person *person1 = new Person();
    Person *person2 = new Person();

    //    unique_ptr<Person> shared_ptr1(person1);
    //  情况①
    shared_ptr<Person> shared_ptr1(person1);
    //  情况②
    shared_ptr<Person> shared_ptr2;
    //  情况③
    shared_ptr2 = shared_ptr1;

    return 0;
}

根据这个分析,我们需要实现,构造函数,拷贝构造函数,= 运算符重载。默认创建对象引用数+1

-> 具体实现


#ifndef TEMPC_CUSTOMPTR_H
#define TEMPC_CUSTOMPTR_H
#pragma once
#include <iostream>
using namespace std;

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

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

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

  // 操作符号重载
    Ptr<T> &operator = (const Ptr<T> & p){
        cout << "操作符重载" << endl;
        ++(*p.count);

        if(--(*count) == 0) {
            if(object){
                delete object;
            }
            delete count;
        }
        object = p.object;
        count = p.count;
        return *this;
    }

    // 拷贝构造函数
    Ptr(const Ptr<T> &p){
        ++(*p.count);
        object = p.object;
        count = p.count;
    }

};


#endif //TEMPC_CUSTOMPTR_H

指针的转换

const_cast

常量指针转换非常量

const Person *p1 = new Person();  

这么做是错误的,p1->name = "David" 但是转换后是可以赋值,并且再次获取name值发生了更改

#include <iostream>

using namespace std;

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

int main() {

    const Person *p1 = new Person();
//    p1->name = "David";// 报错:常量指针,不修改值
// 常量指针转换非常量
    Person *p2 = const_cast<Person *>(p1);
    p2->name = "David";
    cout << p1->name << endl;

    return 0;
}

static_cast

static_cast 指针相关的操作 可以用 static_cast
静态转换看左边(编译期)

#include <iostream>

using namespace std;

class PClass {
public :

    void show() {
        cout << "p show" << endl;
    }
};

class CClass :public PClass{
public :

    void show() {
        cout << "c show" << endl;
    }
};

int main() {


    int n = 88;
    void *pVoid = &n;
    int *number = static_cast<int *>(pVoid);
    cout << *number << endl;
    PClass * pClass = new PClass;
    pClass->show();
    // 静态转换看左边(编译期)
    CClass * cclass = static_cast<CClass *>(pClass);
    cclass->show();

    delete pClass;

    return 0;
}

dynamic_cast

dynamic 字符类多态 运行期 转换

#include <iostream>

using namespace std;

class PClass {
public :

    virtual void show() {
        cout << "p show" << endl;
    }
};

class CClass :public PClass{
public :

    void show() {
        cout << "c show" << endl;
    }
};

int main() {

    PClass * pClass = new CClass();

//    PClass * pClass = new PClass();
    // 动态转换(运行期)
    CClass * cClass = dynamic_cast<CClass *>(pClass);
    if(cClass){
        cout << "success cast";
        cClass->show();
    } else {
        cout << "fail cast" << endl;
    }

    CClass * t = new CClass();
    PClass * p = dynamic_cast<PClass *>(t);
    if(p){
        cout << "p success cast";
        p->show();
    } else {
        cout << "p fail cast" << endl;
    }
    return 0;
}

reinterpret_cast

reinterpret_cast 强制转换 比 static_cast要强大, static_cast能够做的事情,
reinterpret_cast强制转换都可以,同时并且附加 新功能
常见的long 和对象的转换

#include <iostream>

using namespace std;

class Player {
public :

    virtual void show() {
        cout << "Player" << endl;
    }
};



int main() {
    Player * player = new Player();
    long playerValue = reinterpret_cast<long>(player);
    cout << playerValue << endl;
    Player *p = reinterpret_cast<Player *>(playerValue);
    p->show();
    cout << player << endl;
    cout << p << endl;

    return 0;
}

nullptr

nullptr 出现的目的是为了替代 NULL。 同时拥有更多的特性 例如:可以调用到指针参数的函数。

前面多线程补充点,详细说过,nullptr就是解决 NULL 宏定义在C++的二义性

void test(int* i){
    
}
void test(int i){
    
}
//现在调用哪一个test? test(int)
test(9);

//调用test(int* i)
test(nullptr); 
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容