共享指针(std::shared_ptr)实现了共享指针的所有权—只要存在对它的有效引用,它们就使该对象保持活动状态,因为没有单个所有者。通常使用引用计数来完成,这意味着相对于唯一指针,它们具有额外的运行时开销。同样,关于共享所有权的推理比关于专有所有权的推理要困难得多-破坏点变得不确定性强。
#include <iostream>
#include <memory>
// shared_ptr顾名思义是多个指针指向一块内存。
// 被管理对象有一个引用计数,这个计数记录在每个指针上,几个shared_ptr指向它,这个数字就是几,当没有任何shared_ptr指向它时,引用计数为0,这时,自动释放对象。
// 其功能在于当所有指针都释放(或是不再指向对象)的时候,自动释放对象
class Test {
public:
// 无参构造函数
Test();
// 有参数的构造函数
explicit Test(int a);
// 析构函数
~Test();
};
Test::Test() {
std::cout << "无参构造函数" << std::endl;
}
Test::Test(int a) {
std::cout << "有参构造函数,a=" << a << std::endl;
}
Test::~Test() {
std::cout << "析构函数" << std::endl;
}
// make_shared函数来创建对象,而不要手动去new,这样就可以防止我们去使用原始指针创建多个引用计数体系。
int main(int argc, const char * argv[]) {
auto p1 = new Test; // 划分堆空间
std::shared_ptr<Test> sp(p1); // 创建智能指针
std::cout << sp.use_count() << std::endl; // 打印引用计数
{
std::shared_ptr<Test> sp2(sp); // 创建另一个智能指针
std::cout << sp.use_count() << std::endl; // 打印引用计数
} // sp2生命周期结束,sp引用计数减1
std::cout << sp.use_count() << std::endl; // 打印引用计数
auto mp = std::make_shared<int>(); // 分配堆空间,创建智能指针
auto mp2 = mp; // 创建另一个智能指针
std::cout << mp2.use_count() << std::endl;
return 0;
}
空指针
空指针不指向任何对象,在试图使用一个指针前可以先检查指针是否为空。
得到空指针最直接的办法就是用字面值nullptr来初始化指针。
// 空指针的创建方法
int *p1 = nullptr;
int *p2 = 0;
// 需要导入 #include cstdlib
#include <cstdlib>
int *p3 = NULL; // 等价于 int *p3 = 0;
// 不能直接将int变量赋值给指针
int zero = 0;
pi = zero; //错误:不能直接赋值!
void* 指针
void*是一种特殊的指针类型,可用于存放任意对象的地址。一个void* 指针存放着一个地址,但是该地址中存放的是什么类型的对象并不透明。
指向指针的指针
一般来说,声明符中修饰符的个数没有限制,当有多个修饰符连写在一起时,按照其逻辑关系详加解释即可。
指针是内存中的对象,有自己的地址,允许把指针的地址再存放到另一个指针中。
通过*的个数可以区分指针的级别,即为 **表示指向指针的指针,***表示指向指针的指针的指针。
int ival = 1024;
int *p = &ival; // p指向一个int型的数
int **pp = &p; // pp指向一个int型的指针
// 解引用 int型指针会得到一个int型的数,解引用指向指针的指针会得到一个指针。
指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。
// 指针和const
const double pi = 3.14; // pi是常量,值不能改变
double *ptr = π // 错误:ptr是一个普通指针
const double *cptr = π // 正确:cptr可以指向一个双精度常量
*cptr = 42; // 错误:不能给*cptr赋值
double dval = 3.14; // dval是一个双精度浮点数,它的值可以改变
cptr = &dval; // 正确:但是不能通过cptr改变dval的值 可以通过其他途径修改dval的值
指针和数组
数组的元素也是对象,对数组使用下标运算符得到该数组指定位置的元素。对数组的元素使用取地址符能得到指向该元素的指针。
数组还有一个特性:在用到数组名字的地方,很多时候编译器会自动地把数组替换为一个指向数组首元素的指针。(数组名被解释为指向该数组的第一个元素的指针)
// 指针和数组
string nums[] = {"one", "two", "three"};
string *p = &nums[0];
string *p2 = nums;
cout << "p中存放的地址:" << p << endl;
cout << "p2中存放的地址:" << p2 << endl;
cout << "p的解引用:" << *p << endl;
cout << "p2的解引用:" << *p2 << endl;
// auto推断和decltype
int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto ia2(ia);
cout << "ia2的类型:" << typeid(ia2).name() << endl;
// ia2 = 42;
*ia2 = 42;
auto ia3(&ia[0]);
cout << "ia2中存放的地址:" << ia2 << endl;
cout << "ia3中存放的地址:" << ia3 << endl;
cout << "ia2的解引用:" << *ia2 << endl;
cout << "ia3的解引用:" << *ia3 << endl;
decltype(ia) ia4 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// ia4 = p; // 错误:不能用整形指针给数组赋值
cout << "ia4的类型:" << typeid(ia4).name() << endl;