C++|内存管理|智能指针别名构造函数
本文参考ISO文档
在C++编程实践中, 几乎所有人都或多或少地使用过智能指针.但是在智能指针的一个小角落, Alias Constructor大概不为多少人所知.
根据stackoverflow上的一个讨论, Alias Constructor被描述如下
Additionally, shared_ptr objects can share ownership over a pointer while at the same time pointing to another object. This ability is known as aliasing (see constructors), and is commonly used to point to member objects while owning the object they belong to. Because of this, a shared_ptr may relate to two pointers:
- A stored pointer, which is the pointer it is said to point to, and the one it dereferences with operator*.
- An owned pointer (possibly shared), which is the pointer the ownership group is in charge of deleting at some point, and for which it counts as a use.
Generally, the stored pointer and the owned pointer refer to the same object, but alias shared_ptr objects (those constructed with the alias constructor and their copies) may refer to different objects.
什么意思呢,在谈Alias Constructor之前, 我们先简略了解一下shared_ptr实现机制, shared_ptr由两个指针构成, 一个指向控制块, 一个指向存储的指针. 控制块会控制其存储指针的生存期. 但是, 重点来了, 控制块中存储的指针未必是sharedptr存储的指针.
事实上, shared_ptr中存储的控制块指针仅仅只是一个非模板基类, 而存储指针/删除器/分配器等特征都通过指向派生出的模板子类(多态)而体现. 这里后面的特征通过了Type erasure 而逃过了编译器的类型检查.
内部控制块的模板参数和外部智能指针的模板参数毫无干系, 因此控制块指针和存储指针本质上是完全独立的.
那么,为什么C++需要用如此复杂的方式去实现呢?
一个原因是设计者认为删除器/分配器等非必需的工具不应该影响到指针的类型
另一个原因就要讲到这里的Alias了.
Alias,化名, 在这里意为两者具有相同的生命周期.
其语法为shared_ptr<element> ptr(Tptr, element*)
在这里尽管Tptr具有模板参数为T, 但是ptr(模板参数为element)仍旧可以共享其控制块.
在Stackoverflow上有这样一个例子.
struct Bar {
// some data that we want to point to
};
struct Foo {
Bar bar;
};
shared_ptr<Foo> f = make_shared<Foo>(some, args, here);
shared_ptr<Bar> specific_data(f, &f->bar);
// ref count of the object pointed to by f is 2
f.reset();
// the Foo still exists (ref cnt == 1)
// so our Bar pointer is still valid, and we can use it for stuff
some_func_that_takes_bar(specific_data);
对象的成员和对象本身,显然的确应该共享生存期,但有人可能会问了, 为什么不用->调用成员呢, 似乎这并非刚需.
在思考很久之后, 不同类型对象共享生存期的情况映入了我的脑中.
struct Base1 { int base1=0; };
struct Base2 { int base2=0; };
struct A:Base1,Base2{};
int main()
{
auto ptr = new A;
shared_ptr<Base1>ptr1 ((Base1*)ptr);
//shared_ptr<Base2>ptr2 ((Base2*)ptr);
//内存泄漏,ptr2已经被释放:-572662307end crash
shared_ptr<Base2>ptr2(ptr1, (Base2*)(ptr));
//化名,ptr2未被释放:0end
ptr1.reset();
cout << ptr2->base2;
cout << "end";
return 0;
}
在C++中,一种特殊的情况在于多继承, 当我们面向接口(类型)编程时, 如果我们使用接口指针去操纵对象,就不得不遇到不同接口的智能指针指向同一个对象了. 一旦我们没有使用Alias, 由于不同类型指针无法共享控制块, 这势必会产生内存泄漏, 而通过Alias, 我们则完美地避免了这一情况.
相关文章
- 金融服务领域的大数据:即时分析
- 影响大数据、机器学习和人工智能未来发展的8个因素
- 从0开始构建一个属于你自己的PHP框架
- 如何将Hadoop集成到工作流程中?这6个优秀实践必看
- SEO公司使用大数据优化其模型的5种方法
- 关于Web Workers你需要了解的七件事
- 深入理解HTTPS原理、过程与实践
- 增强分析:数据和分析的未来
- PHP协程实现过程详解
- AI专家:大数据知识图谱——实战经验总结
- 关于PHP的错误机制总结
- 利用数据分析量化协同过滤算法的两大常见难题
- 怎么做大数据工作流调度系统?大厂架构师一语点破!
- 2019大数据处理必备的十大工具,从Linux到架构师必修
- OpenCV中的KMeans算法介绍与应用
- 教大家如果搭建一套phpstorm+wamp+xdebug调试PHP的环境
- CentOS下三种PHP拓展安装方法
- Go语言HTTP Server源码分析
- Go语言HTTP Server源码分析
- 2017年4月编程语言排行榜:Hack首次进入前五十