//
//  SmartPointer.cpp
//  test_cpp_protocol_01
//
//  Created by jeffasd on 2016/10/24.
//  Copyright © 2016年 jeffasd. All rights reserved.
//

#include "SmartPointer.h"
#include <stdio.h>
#include <memory.h>
#include <string>

#include "Person.h"

SmartPointer::SmartPointer()
:_data(3)
{
    printf("SmartPointer constructor\n");
    
    _data = int(3);
}

SmartPointer::~SmartPointer() {
    printf("SmartPointer destructor\n");
}

void SmartPointer::showName() {
    
    printf("smartPointer showName\n");
    
    // 智能指针的各种初始化方法
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(3);
    std::shared_ptr<Student> sharedPtrOne(new Student());
    std::shared_ptr<Student> sharedPtrTwo = std::make_shared<Student>();
    sharedPtrTwo->writeWorldWithString("hello wolrd");
    std::shared_ptr<Student> _imp = std::shared_ptr<Student>(new Student());
    std::shared_ptr<Student> sharedPtrThree(sharedPtrOne); // copy 引用计数+1

    /**
    每个shared_ptr都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。
    例如,当用一个shared_ptr初始化另一个shred_ptr,或将它当做参数传递给一个函数以及作为函数的返回值时,它
    所关联的计数器就会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的
    shared_ptr离开其作用域)时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理
    的对象。
     */
    
    Student *xiaoming = new (std::nothrow) Student ();
    Student *xiaohua = new (std::nothrow) Student ();
    // 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
    std::shared_ptr<Student> sharedPtrFive(xiaoming); // 当一个对象指针使用智能指针时 以后不要再使用此对象指针 而要使用智能指针
    // warring 对一个原始指针初始化多个shared_ptr,会造成二次释放同一个内存,此用法不正确
    // std::shared_ptr<Student> sharedPtrErrorUse(xiaoming);
    
    xiaoming->writeWorldWithString("hello xiaoming"); // 使用智能指针之后最好不要再使用原始指针 不推荐这样用
    
    //返回与sharedPtrFive共享xiaoming对象的智能指针数量,这个操作可能会比较慢,一般调试的时候用,正式情况下一般不用
    //一个bool函数 如果只有一个引用计数则返回true,如果不是则返回false
    bool isUnique = sharedPtrFive.unique();
    printf("sharedPtrFive is Unique %s", isUnique ? "true" : "false");
    
    // use_count()获取有多少个智能指针共享同一个内部数据指针
    printf("reference is %ld\n", sharedPtrFive.use_count());
    
    // shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
    // 初始化。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr<int> p4 = new int(1);的写法是错误的
    // 拷贝和赋值根据是否有新的智能指针指向了同一个对象,如果有新增智能指针指向了同一个对象,引用计数加1 否则引用计数不加1
    // 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
    // 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。
    
    // 拷贝和赋值根据是否有新的智能指针指向了同一个对象,如果有新增智能指针指向了同一个对象,引用计数加1 否则引用计数不加1
    // 有新的sharedPtrSix指向了xiaoming对象 引用计数加1
    std::shared_ptr<Student> sharedPtrSix = sharedPtrFive; // copy 赋值 引用计数+1
    // std::shared_ptr<Student> sharedPtrSix(sharedPtrFive); // copy 赋值 引用计数+1
    
    // use_count 引用计数详解:指的是有多少个shared_ptr指向同一个对象 此处use_count为2 sharedPtrSix和sharedPtrFive 均指向xiaoming对象
    // 没有新的智能指针指向了xiaoming对象 引用计数不加1
    sharedPtrFive = sharedPtrSix;
    
    printf("reference is %ld\n", sharedPtrFive.use_count());
    printf("reference is %ld\n", sharedPtrSix.use_count());
    
    sharedPtrFive->writeWorldWithString("sharedPtrFive");
    sharedPtrSix->writeWorldWithString("sharedPtrSix");
    
    // 重新赋值
    // sharedPtrFive.reset(xiaohua);
    
    sharedPtrFive.reset(); // 将sharedPtrFive的引用计数减一 引用计数最小值为0
    sharedPtrFive.reset(); // 将sharedPtrFive的引用计数减一 引用计数最小值为0
    sharedPtrFive.reset(); // 将sharedPtrFive的引用计数减一 引用计数最小值为0
    
    printf("reference is %ld\n", sharedPtrFive.use_count());
    printf("reference is %ld\n", sharedPtrSix.use_count());
    
    sharedPtrSix.reset(); // 将sharedPtrSix的引用计数减一 引用计数最小值为0
    
    // unique_ptr是一个独占的智能指针,即unique_ptr不支持复制,但是支持通过move转移内部指针
    // Student *xiaogang = new (std::nothrow) Student ();
    // std::unique_ptr<Student> uniquePtr(xiaogang);
    std::unique_ptr<Student> uniquePtr = std::make_unique<Student>();
    std::unique_ptr<Student> uniquePtrOne = std::make_unique<Student>();
    // release()释放原来智能指针指向的指针,只负责转移控制权,不负责释放内存,常见的用法
    // 所以如果单独用: 则会导致uniquePtr丢了控制权的同时,原来的内存得不到释放
    // uniquePtr.release()
    // 注意release()只转移控制权,并不释放内存,而reset和=nullptr操作会释放原来的内存
    // uniquePtr.reset();// 释放p原来的对象,并将其置为nullptr,
    uniquePtr = nullptr; // 等同于uniquePtr.reset();
    uniquePtr = std::move(uniquePtrOne); // 转移控制权
    
    printf("reference \n");
    
    // weak_ptr可以看做shared_ptr的助手,weak_ptr要和shared_ptr配套一起使用。当创建一个weak_ptr时,要用一个shared_ptr来初始化它。
    // 复制shared_ptr是会增加内部数据的引用计数,但是复制weak_ptr时,以及由shared_ptr构造weak_ptr时,是不会增加引用计数的;且weak_ptr没有重载*、->操作符,所以不能通过*、->操作符操作智能指针的内部数据
    /**
    因为weak_pt不增加引用计数,我们可以任意构造weak_ptr,任意释放weak_ptr,却不会影响智能指针中内部数据的释放(内部数据何时释放,只取决于shared_ptr)。那么weak_ptr有什么用呢?weak_ptr可以用来监看shared_ptr:
    weak_ptr::use_count()查看有多少个shared_ptr共享同一个内部数据
    weak_ptr::expired判断shared_ptr是否有效,即shared_ptr内部数据是否被释放
    weak_ptr是否可以监看shared_ptr中的内部数据呢?因为weak_ptr是弱指针,所以不能直接访问,但是可以通过weak_ptr::lock间接访问。
     */
    std::shared_ptr<Student> sharedPtrSeven = std::make_shared<Student>();
    std::weak_ptr<Student> weakPtr = sharedPtrSeven;
    
    printf("weak_ptr reference is %ld\n", weakPtr.use_count());
    
    // 相当于把weak_ptr转换为shared_ptr,然后通过shared_ptr去访问。
    std::shared_ptr<Student> sharedPtrLock = weakPtr.lock(); // 将lock返回的智能指针赋值给sharedPtrLock 引用计数会加1
    printf("weak_ptr reference is %ld\n", weakPtr.use_count());
    printf("sharedPtr reference is %ld\n", sharedPtrSeven.use_count());
    weakPtr.lock(); // 没有将lock函数返回的智能指针赋值给任何其他智能指针 引用计数不会加1
    printf("weak_ptr reference is %ld\n", weakPtr.use_count());
    printf("sharedPtr reference is %ld\n", sharedPtrSeven.use_count());
    
    printf("reference");
}