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

注:编码工具是CLion+Cygwin64

目录

智能指针

shared_ptr

引用计数器

循环引用

weak_ptr

unique_ptr

仿写智能指针

类型转换

const_cast

static_cast

dynamic_cast

reinterpret_cast

#define

宏函数

#if、#elif、#else、#endif

#ifdef、#ifndef、#undef

实践案例


智能指针

        智能指针可以用于自动回收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

#define

        代码中使用宏变量,在预处理阶段会将变量替换为值。

#include <iostream>
using namespace std;

#define TAG "HONG"
int main() {

    // 预处理阶段,会将TAG替换为"HONG"
    cout << TAG << endl;
    return 0;
}

输出:

HONG

宏函数

#include <iostream>
using namespace std;

#define SHOW(V) cout << V << endl
#define ADD(A, B) A + B
#define MUL(A, B) A * B
int main() {

    SHOW(1);// 预处理后,会替换为cout << 1 << endl;
    SHOW("宏函数");// 预处理后,会替换为cout << "宏函数" << endl;
    SHOW(3.14);// 预处理后,会替换为cout << 3.14 << endl;
    cout << endl;
    cout << ADD(1, 2) << endl;// 预处理后,会替换为cout << 1 + 2 << endl;
    cout << ADD(1.03, 2) << endl;// 预处理后,会替换为cout << 1.03 + 2 << endl;
    cout << endl;
    cout << MUL(1, 2) << endl;// 预处理后,会替换为cout << 1 * 2 << endl;
    cout << MUL(1 + 1, 2 + 2) << endl;// 预处理后,会替换为cout << 1 + 1 * 2 + 2 << endl;由于是直接替换,不会出现想象中的cout << 1 * 4 << endl;
    return 0;
}

输出:

1
宏函数
3.14

3
3.03

2
5

#if、#elif、#else、#endif

        #if必须对应#endif,否则报错。

#include <iostream>
using namespace std;

int main() {
#define flag 1
#if flag
    cout << "真的" << endl;
#elif !flag
    cout << "假的" << endl;
#endif

#if flag
    cout << "true" << endl;
#else
    cout << "false" << endl;
#endif
    return 0;
}

输出:

真的
true

#ifdef、#ifndef、#undef

#include <iostream>
using namespace std;

int main() {

#ifndef CMACRO// 判断是不是没定义宏 CMACRO
#define CMACRO// 只有没定义宏 CMACRO,才会执行此句
#endif

#ifdef CMACRO// 判断是不是定义了宏 CMACRO
    cout << "定义了CMACRO" << endl;// 只有定义了宏才会执行此句
#endif

#ifdef CMACRO
#undef CMACRO // 取消定义宏 CMACRO
#endif

#ifdef CMACRO
    cout << "定义了CMACRO" << endl;
#else
    cout << "没定义CMACRO" << endl;
#endif

    return 0;
}

输出:

定义了CMACRO
没定义CMACRO

实践案例

        切换测试和线上环境

#include <iostream>
using namespace std;

#ifndef isRelease
#define isRelease 0
#endif

#if isRelease
#define RELEASE
#else
#define DEBUG
#endif

int main() {
#ifdef RELEASE
    cout << "是线上环境" << endl;
#else
    cout << "是测试环境" << endl;
#endif
    return 0;
}

输出:

是测试环境

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值