Skip to content

Instantly share code, notes, and snippets.

@vacing
Last active September 11, 2025 08:01
Show Gist options
  • Select an option

  • Save vacing/c098fbbbd5f9151ff1ee73748ff96edc to your computer and use it in GitHub Desktop.

Select an option

Save vacing/c098fbbbd5f9151ff1ee73748ff96edc to your computer and use it in GitHub Desktop.
shared_pointer wrapper for nonprimitive c struct .
#include <memory>
#include <iostream>
#include <cstdio>
/**
* @file safe_ptr.h
* @author vacingfang
* @version 0.1, 2025-08-14, init version
* @version 0.11, 2025-08-20, more comments
* @brief
* This is a shared_ptr wrapper for c-style non-primitive struct. These type
* have their own release function to relese the resources they hold, such as
* memory, file descriptor and so on.
* Deleter class must process nullptr, such as
* ```c++
* struct TestDeleter {
* void operator()(std::nullptr_t) {} // for nullptr
* ...
* };
* ```
* Usage of SafeSPtr
* ```
* usging TestSPtr = SafeSptr<Test, TestDeleter>;
* TestSPtr tsptr{new Test};
* // TestSPtr tsptr = new Test(); // not support, only explicit construct
* ```
*/
template<typename T, typename Deleter>
struct SafeSPtr : public std::shared_ptr<T> {
// default, Deleter must process nullptr !!
SafeSPtr() : std::shared_ptr<T>(nullptr, Deleter()) {}
// default deleter, will destroy unique_ptr assign
// SafePtr() : std::shared_ptr<T>() {} // wrong !!
// big five
SafeSPtr(const SafeSPtr&) = default;
SafeSPtr(SafeSPtr&&) noexcept = default;
SafeSPtr& operator=(const SafeSPtr&) = default;
SafeSPtr& operator=(SafeSPtr&&) noexcept = default;
virtual ~SafeSPtr() = default;
// Explicit constructor: prevents implicit conversion from raw pointer
// which could otherwise cause unintended ownership transfer and unexpected deletion.
// keep align with std::shared_ptr<T>
explicit SafeSPtr(T* p) : std::shared_ptr<T>(p, Deleter()) {}
// unique pointer
SafeSPtr(std::unique_ptr<T>&& up) : SafeSPtr(up.release()) {}
SafeSPtr(std::unique_ptr<T, Deleter>&& up) : SafeSPtr(up.release()) {}
SafeSPtr& operator=(std::unique_ptr<T>&& up) {
this->reset(up.release());
return *this;
}
SafeSPtr& operator=(std::unique_ptr<T, Deleter>&& up) {
this->reset(up.release());
return *this;
}
// no shared_ptr style use
template<typename D> SafeSPtr(T*, D) = delete;
// no common shared_ptr<T>
SafeSPtr(std::shared_ptr<T>&& sp) = delete;
SafeSPtr(const std::shared_ptr<T>&) = delete;
};
/**
* @brief build command
* g++ safe_ptr.cpp -std=c++14 -o safe_ptr -g && ./safe_ptr
*/
static int struct_ind = 0;
struct Cstruct {
int *pi;
int ind;
Cstruct() {
ind = ++struct_ind;
printf("init [%d] %p\n", ind, this);
this->pi = (int *)malloc(10);
}
};
void destroy(Cstruct* p, int type) {
printf("destroy [T%d][%d] %p\n", type, (p? p->ind : -1), p);
if (p && p->pi) free(p->pi);
}
struct CDeleter {
void operator()(std::nullptr_t) {} // for nullptr
void operator()(Cstruct *p) {
destroy(p, 1);
}
void operator()(int *p) {
}
};
struct CDeleter2 {
void operator()(std::nullptr_t&) {} // for nullptr
void operator()(Cstruct *p) {
destroy(p, 2);
}
void operator()(int *p) {
}
};
using CstructSPtr = SafeSPtr<Cstruct, CDeleter>;
using CstructSPtr2 = SafeSPtr<Cstruct, CDeleter2>;
using CstructPtr = std::unique_ptr<Cstruct, CDeleter>;
int main() {
CstructSPtr pnull;
CstructSPtr p1(new Cstruct);
CstructSPtr p2(new Cstruct);
printf("p2 = p1\n");
p2 = p1;
std::shared_ptr<Cstruct> sp3;
{
CstructSPtr p3(new Cstruct);
printf("sp3 = p3\n");
sp3 = p3;
}
CstructSPtr up4 = std::make_unique<Cstruct>();
CstructSPtr up5 = std::make_unique<Cstruct>();
printf("pnull = up4\n");
pnull = up4;
// CstructPtr up5 = std::make_unique<Cstruct>(); // 失败,类型不兼容
// CstructPtr up5 = std::make_unique<Cstruct, CDeleter>(); // make_unique() 不能指定 deleter
CstructSPtr2 p6(new Cstruct);
// CstructSPtr ptmp = p6; // 退化成普通的 std::shared_ptr<T> 类型构造,已delete
CstructSPtr p7;
// p7 = p6; // 没有CstructSPtr2 和 CstructSPtr 之间赋值函数,p6 会尝试转换成 CstructSPtr 对象后再赋值
// 转换时退化成普通的 std::shared_ptr<T> 类型构造,已delete
printf("p1.reset()\n");
p1.reset();
printf("p2.reset()\n");
p2.reset();
// 不支持,防止函数传参 误将raw 指针释放
// CstructSPtr p8 = new Cstruct();
// std::shared_ptr<Cstruct> p8 = new Cstruct();
CstructSPtr pnullptr;
printf("end\n");
return 0;
}
@vacing
Copy link
Author

vacing commented Aug 22, 2025

g++ safe_ptr.cpp -std=c++14 -o safe_ptr -g && ./safe_ptr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment