指针用法及知识点总结

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


//指针基础
void test1()
{

    double d = 9.0;

    cout << "------1------" << endl;
    //取址操作
    cout << &d << endl;


    cout << "------2------" << endl;
    //声明指针及指针赋值,读取指针
    //应该把 double* 看作是一个复合类型来理解(类比数组,指针是基于其他类型的)
    double* dp;
    dp = &d;
    //也可以这样初始化
    //double* dp = &d;

    // *dp 与 d 都代表值 9.0 ; dp 与 &d 都代表地址
    cout << dp << endl;
    cout << *dp << endl;

    cout << "------3------" << endl;
    //对 dp 的理解,指针赋值
    // dp 是指针变量,它也有自己的地址,下面输出指针 dp 的地址
    cout << &dp << endl;
    /*
    在地址 &dp 中储存的是 &d 的地址(一般用一个16进制数字来表示)
    所以 dp 代表地址,本质是指针(变量);
    加上间接值运算符*后, *dp 代表值,本质是一个double变量
    */

    //如果对 *dp 赋值,这样将修改地址为 dp 的内存区域储存的值
    *dp = *dp + 1;
    cout << dp << endl;
    cout << *dp << endl;
    //变量 d 也指向地址为 dp 的内存区域
    cout << d << endl;

    cout << "------4------" << endl;
    //错误的指针使用
    /*
    int* wrongPoint;
    *wrongPoint = 233333;
    cout << wrongPoint << endl;
    cout << *wrongPoint << endl;
    */
    /*
    与上面的正确步骤相比较,这里少了给 wrongPoint 赋值地址的过程;
    而地址 &wrongPoint 是什么值并不清楚,但程序都将其解释为地址,并将地址为 wrongPoint 的内存区域赋值为 233333
    这将造成不可估量的恶劣后果
    必须在使用 *wrongPoint 前,将 wrongPoint 初始化为一个合适的地址
    */

    cout << "------5------" << endl;
    //指针与数字
    //上面提到地址一般用一个16进制数字来表示,但指针与数字 int 在c++是不一样的类型,不能直接赋值
    //dp = 0xB8000000;
    //可以通过类型转换来 将 int 当作 地址 使用
    dp = (double*)0xB8000000;
    cout << dp << endl;
}

//指针与动态内存
void test2()
{
    cout << "------1------" << endl;
    //使用指针访问动态内存
    /*
    在test1中,指针只是作为变量的别名
    变量是编译期分配的有别称的内存
    而想要在运行期动态分配未命名的内存,只能通过指针来访问

    下面使用 new 来动态分配内存
    new 将找到大小合适存放 int 的内存,并返回该内存的地址
    然后,需要将该地址赋值给相应的指针类型来使用
    */
    int* intPoint = new int;
    *intPoint = 1001;
    cout << intPoint << endl;
    cout << *intPoint << endl;
    cout << sizeof(*intPoint) << endl;

    cout << "------2------" << endl;
    //动态内存释放
    //当动态内存使用完毕后,需要 delete 来释放,将该内存归还给内存池
    //delete 只能用于 new 分配的内存,且同一块内存只能 delete 一次,即 new 与 delete 是配对使用的
    delete intPoint;
    //注意 delete 真正作用的是内存快,而不是指针,指针并没有被删除,可以重新指向新的内存块
    int* intPoint2 = new int;
    int* intPoint3 = intPoint2;
    *intPoint2 = 2;
    cout << intPoint2 << endl;
    cout << *intPoint3 << endl;

    delete intPoint3;
    cout << "after delete" << endl;
    //delete后,intPoint3不可用(指针的值已经改变),但intPoint2还是指向该内存,并且可访问,但是值已经没有意义了
    cout << intPoint2 << endl;
    cout << *intPoint2 << endl;
    cout << intPoint3 << endl;
    //cout << *intPoint3 << endl;

    //重新分配有可能还是一样的地址,因为该地址已经归还到内存池了
    intPoint2 = new int;
    cout << "renew" << endl;
    cout << intPoint2 << endl;
    cout << *intPoint2 << endl;
}

//数组与指针算术
void test3()
{
    cout << "------1------" << endl;
    //创建动态数组,这里new返回第一个元素的地址
    int* listIntP = new int[10];
    //释放也要带 [] ,表示释放整个数组,而不是第一个元素
    delete[] listIntP;

    cout << "------2------" << endl;
    //指针支持四种运算++、--、 + 、 - ;变化的量等于指针指向类型的字节数
    int *intP = new int;
    cout << intP << endl;
    intP++;
    cout << intP << endl;
    intP = intP - 2;
    cout << intP << endl;

    cout << "------3------" << endl;
    //数组名通常被解释为第一个元素的地址。
    int listInt[3] = {1, 2, 3};
    cout << listInt << endl;
    cout << &listInt[0] << endl;

    cout << "------4------" << endl;
    //数组与指针基本等价,可以这样初始化一个指针
    int* pointInt = listInt;
    //访问元素(在编译器里,pointInt[1] 看作是 *(pointInt + 1))
    cout << pointInt[1] << ", " << listInt[1] << endl;
    cout << *(pointInt + 1) << ", " << *(listInt + 1) << endl;
    *(listInt + 1) = 4;
    pointInt[0] = 5;
    cout << listInt[0] << ", " << pointInt[1] << ", " << listInt[2] << endl;

    cout << "------5------" << endl;
    //数组与指针的区别
    //指针可以修改值,而数组名是常量
    pointInt++;
    cout << *pointInt << endl;
    //listInt++; //不合法

    //使用sizeof,数组得到数组长度,指针是指针长度
    cout << sizeof(pointInt) << endl;
    cout << sizeof(listInt) << endl;


    cout << "------6------" << endl;
    //c风格字符串也是数组,c++里 char数组名,char指针,字符串常量都被解释为字符串第一个字符的地址
    char aa[20] = "aa";
    const char* bb = "bb";
    //cout对象接受到char地址时,会当作字符串打印,直至'\0'
    cout << aa << bb << "cc" << endl;
    //要输出地址,只能对地址类型转换
    cout << (int*)aa << endl;

}

struct book
{
    char name[20];
    double price;
};

//指针与动态结构
void test4()
{
    book* ps = new book;
    //指针访问结构成员用 -> ,也可以用值加 . 来表示
    char nameBook[20] = "sama";
    strcpy_s(ps->name, nameBook);
    ps->price = 20.0;

    cout << (*ps).name << endl;
    cout << (*ps).price << endl;
}


/*  函数用数组名作为参数  */
/*  形式参数 arr[] 其实是一个指针,因为指针和数组名基本等价,可以把 arr 看作数组
    该函数与以下声明等价
    void printList(int* arr, int len)
*/
void printList(int arr[], int len)
{
    cout << arr << endl;
    //输出长度不是数组的长度,因为arr是指针
    cout << sizeof(arr) << endl;

    for (int i = 0; i < len; i++)
    {
        cout << arr[i] << endl;
    }
}

//二维数组的例子
/*     该函数与以下声明等价,该原型表示指向长度为4的数组的指针
    void printList(int (*arr)[4], int len)
*/
void printList2(int arr[][4], int len)
{
    cout << arr[1][1] << endl;
}

void test5()
{
    int arr[] = { 1,2,3 };
    cout << sizeof(arr) << endl;
    printList(arr, sizeof(arr) / sizeof(arr[0]));

    int arr2[2][4] = { {1,2,3,4}, {5,6,7,8} };
    printList2(arr2, 2);
}

//二重(级)指针
void test6()
{
    //存放指针地址的指针
    int tt = 1;
    int* ttp = &tt;
    int** ttpp = &ttp;

    cout << tt << endl;
    cout << *ttp << endl;
    cout << **ttpp << endl;

    int t2 = 2;
    *ttpp = &t2;
    cout << *ttp << endl;
    cout << **ttpp << endl;
}

//函数指针
double pam(int a)
{
    return a * 0.05;
}

void test7()
{
    //函数指针原型
    double (*pf)(int);
    //pam 即 方法pam的地址
    pf = pam;

    //以下两种方法调用都是可以的
    cout << (*pf)(2) << endl;
    cout << pf(1) << endl;
}

//智能指针
//自动执行回收的指针模板类,背后的思想是在类析构时调用delete

class testPtr
{
public:
    testPtr(int a) { tag = a; cout << "new " << a << endl; };
    ~testPtr() { cout << "delete " << tag << endl; };
    int get() { return tag; };
    void setShaerPtr(shared_ptr<testPtr> ptr1) { sPtr = ptr1; };
    void setWeakPtr(weak_ptr<testPtr> ptr1) { wPtr = ptr1; };
    weak_ptr<testPtr> getWeakPtr() { return wPtr; };
private:
    int tag;
    shared_ptr<testPtr> sPtr;
    weak_ptr<testPtr> wPtr;
};

unique_ptr<testPtr> createUniquePtrObj(int a)
{
    unique_ptr<testPtr> ptr(new testPtr(a));
    return ptr;
}



void test8()
{
    cout << "------1------" << endl;
    //在代码块结束后,智能指针unique_ptr将自动释放内存
    {
        unique_ptr<testPtr> uptr(new testPtr(1));
        //智能指针和普通指针一样可以使用解除引用和间接引用
        cout << uptr->get() << endl;
        cout << (*uptr).get() << endl;
    }
    //智能指针不可用于非堆内存
    //testPtr p(2);
    //unique_ptr<testPtr> uPtr2(&p);

    //unique_ptr的所有权是唯一的,一个对象只可以被一个unique_ptr拥有
    //unique_ptr<testPtr> uptr3(new testPtr(3));
    //unique_ptr<testPtr> uptr4;
    //uptr4 = uptr3;  // 编译不通过

    cout << "------2------" << endl;
    //这里的赋值是允许的,因为 createUniquePtrObj 的智能指针生命周期已经结束了,对象仍然只被一个智能指针持有
    unique_ptr<testPtr> uptr5 = createUniquePtrObj(5);
    cout << uptr5->get() << endl;

    cout << "------3------" << endl;
    //可以通过move移交所有权,注意在移交所有权后,uptr6 变成了 nullptr
    unique_ptr<testPtr> uptr6, uptr7;
    uptr6 = createUniquePtrObj(6);
    cout << uptr6 << endl;
    uptr7 = move(uptr6);
    cout << uptr6 << endl;
    cout << uptr7 << endl;
    cout << uptr7->get() << endl;

    cout << "------4------" << endl;
    //与unique_ptr相对,shared_ptr可以共享所有权,shared_ptr通过引用计数管理对象的回收
    {
        shared_ptr<testPtr> sptr1(new testPtr(7));
        cout << sptr1.use_count() << endl;
        {
            shared_ptr<testPtr> sptr2 = sptr1;
            cout << sptr2.use_count() << endl;
        }
        cout << sptr1.use_count() << endl;
    }

    cout << "------5------" << endl;
    //shared_ptr存在环引用问题
    {
        shared_ptr<testPtr> sptr2(new testPtr(8));
        cout << sptr2.use_count() << endl;
        {
            shared_ptr<testPtr> sptr3 = sptr2;
            cout << sptr2.use_count() << endl;
            sptr2->setShaerPtr(sptr3);
            cout << sptr2.use_count() << endl;
        }
        cout << sptr2.use_count() << endl;
        //结束,testPtr(8)不会析构,此时 use_count = 1
    }

    cout << "------6------" << endl;
    //引入weak_ptr避免环引用
    {
        shared_ptr<testPtr> sptr4(new testPtr(9));
        cout << "count:" << sptr4.use_count() << endl;
        {
            shared_ptr<testPtr> sptr5 = sptr4;
            cout << "count:" << sptr4.use_count() << endl;
            //setWeakPtr 入参是 weak_ptr,shared_ptr 可以自动类型转换为 weak_ptr
            sptr4->setWeakPtr(sptr5);
            cout << "count:" << sptr4.use_count() << endl;
        }
        cout << "count:" << sptr4.use_count() << endl;

        weak_ptr<testPtr> wptr = sptr4->getWeakPtr();

        //控制对象回收,注释掉weak_ptr的行为会不一样
        sptr4.reset();
        cout << "count:" << sptr4.use_count() << endl;

        // weak_ptr 无法保证对象的有效性,一般用 lock() 获取shared_ptr对象,如果对象已经不可用,shared_ptr为空;
        // 也可以通过 expired() 获取是否已经销毁
        shared_ptr<testPtr> sptrToWPtr = wptr.lock();
        cout << sptrToWPtr << endl;
        if (sptrToWPtr)
        {
            cout << sptrToWPtr->get() << endl;
        }
        cout << wptr.expired() << endl;
    }

    cout << "------end------" << endl;
}

int main()
{
    //test1();
    //test2();
    //test3();
    //test4();
    //test5();
    //test6();
    //test7();
    test8();

    return 0;
}

 

上一篇:详细介绍mysql索引类型:FULLTEXT、NORMAL、SPATIAL、UNIQUE


下一篇:unique_ptr