极端理想的情况是这样的。
然而现实中:
1:你需要维护大量c++03甚至更老的代码,给它fix或者叠加功能……
2:你需要引用到很多已完成的代码库,而这些库的接口甚至是纯c的。
3:在以前,有时候会有一些逻辑/技巧会把裸指针转为整形进行运算和处理:例如说我见过一个rbt实现,把指针的最低bit拿来指示颜色(因为内存对齐的缘故,从堆里申请出来的内存,最低bit一般都为0)。还有本机多进程服务,把在共享内存的指针当做整形传给对方(双方在mmap时指定同一个基址)。
遇到了这些,你连改写都不一定那么容易改写。
Rust 我不太了解,但 Smart Pointer 并没有从根本上解决 C++ 的内存安全问题。说白了,程序员用不好、不会用,依然轻轻松松造成内存安全问题,随手就来几个例子:
1、std::shared_ptr 被提前释放:
void process(std::shared_ptr<int> svp) {} int main(int argc, char** argv) { int* vp = new int(10); process(std::shared_ptr<int>(vp)); std::cout << *vp << std::endl; // pointer "vp" has already been released. return 0; }
2、使用栈对象初始化智能指针,造成悬挂指针:
auto process() { int v = 10; int* vp = &v; return std::shared_ptr<int>(vp); } int main(int argc, char** argv) { std::cout << *process() << std::endl; // dangling pointer. return 0; }
3、std::shared_ptr 造成的循环引用:
struct C { ~C() { std::cerr << "destructor
"; } std::shared_ptr<C> sp; }; int main(int argc, char **argv) { auto p = std::make_shared<C>(), q = std::make_shared<C>(); p->sp = q; q->sp = p; return 0; }
4、不当用法造成的潜在内存泄露:
bool complicatedCompute() { /* ... */ return true; } // potential memory leak; auto process(std::shared_ptr<int>, bool) {} int main(int argc, char** argv) { process(std::shared_ptr<int>(new int(10)), complicatedCompute()); return 0; }
BTW. 除了上述想到的几种场景,其他的还包括 std::shared_ptr 与 std::weak_ptr 联合使用不当所可能导致的潜在内存问题:比如,使用 std::make_shared 方式构造的 std::shared_ptr 对象,其控制块内存只会在对应 std::weak_ptr 引用计数完全清零时,才会将堆内一次性分配的整块内存完全释放。因此这就可能造成内存释放的不连续性,中间产生的 time gap 在某些应用场景下可能会带来潜在问题。另外,new + std::shared_ptr 构造方式虽然不存在上述问题,并且支持自定义 deleter,但却可能导致潜的在内存泄露以及智能指针对象构造效率的问题。因此,在 C++ 中至少暂时还没有特别完美的方案来真正“智能地”管理内存和指针,并同时兼顾性能、安全、使用成本、维护成本等诸多方面问题。
最后补充一句:C++11 曾经有过标准以支持“最小垃圾回收及安全派生指针”,但很可惜,据我所知,从 GUN 到 Clang 目前还没有任何一款编译器支持。
Update: