#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;
}