使用作为参数返回的指针的最佳做法是什么
What is the best practice to consume a pointer returned as an argument
通常,当创建指向对象的指针并从函数返回时,您使用 unique_ptr 来确保它在范围末尾被删除。
CustomType* GetTheObject(); // Let's say this the function signature and it returns a new object
void Main() {
auto object = make_unique(GetTheObject());
object->DoSomething();
// Then the object is deleted automatically
}
如果函数签名是这样的怎么办
bool GetTheObject(CustomType** object);
我可以想象一种相当冗长的消费方式
void Main() {
// Declare a pointer which we never need
CustomType* object_ptr;
if(GetTheObject(&object_ptr)) {
// Then create a unique_ptr out of it
auto object = make_unique(object_ptr);
object->DoSomething();
// Then the object is deleted automatically
}
}
在这种情况下,是否有更好的推荐方法来使用对象。我可以考虑另一个实现 & 运算符的 unique_ptr2 class 然后像
一样使用它
unique_ptr2 object;
if(GetTheObject(&object)) {
// use it
}
是否有现成可用的 unique_ptr2 实现可以做到这一点?感觉还是不太理想。有没有更好的方法?
我认为返回 std::unique_ptr
比返回原始指针更安全,因为返回原始指针有调用代码意外泄漏对象的风险。我建议这样做:
#include <iostream>
#include <memory>
class CustomType
{
// ...
};
std::unique_ptr<CustomType> GetTheObject()
{
if ((rand()%2) != 0) return std::make_unique<CustomType>();
return std::unique_ptr<CustomType>(); // nothing to return, sorry
}
int main(int argc, char ** argv)
{
if (std::unique_ptr<CustomType> p = GetTheObject())
{
std::cout << "Got the object!" << std::endl;
}
return 0;
}
如果您不得不忍受一个您不喜欢其形状且无法更改的现有函数,您可以将丑陋隐藏在包装函数中,然后改为调用包装函数:
std::unique_ptr<CustomType> PrettyGetTheObject()
{
CustomObject * obj;
if (GetTheObject(&obj)) return std::unique_ptr<CustomObject>(obj);
return std::unique_ptr<CustomType>(); // nothing to return, sorry
}
一种可能的方法是使用包装函数。示例:
bool GetOriginal(char **pptr);
bool GetWrapped(std::unique_ptr<char> * puptr) {
bool result;
if (puptr != nullptr) {
char * cptr;
result = GetOriginal(&cptr);
*puptr = std::make_unique(cptr);
} else {
result = GetOriginal(nullptr);
}
return result;
}
这假定了传递 null 的常用模式以避免获取管理指针(例如,如果您只对 return 值感兴趣)。
如果将 null 传递给原始函数不是其 api 的一部分,那么您当然可以使用对 std::unique_ptr
的引用而不是原始指针。
如果你有很多这样的函数,你当然也可以为此编写一个通用的包装函数:
template<typename Fn, typename T>
bool wrap(Fn fn, std::unique_ptr<T> * puptr) {
bool result;
if (puptr != nullptr) {
T * cptr = nullptr;
result = fn(&cptr);
*puptr = std::make_unique(cptr);
} else {
result = fn(nullptr);
}
return result;
}
// usage: wrap(GetOriginal, &some_unique_ptr)
如果原始函数需要更多参数,则使用 std::bind
或 lambda。
我可能想编写自动转换 to/from 唯一指针的代码。我们从具有相同签名的现有函数 "automatically" 生成一个新函数,但是 T*
return 值是 unique_ptr<T>
并且 T**
参数是 unique_ptr<T>*
参数。
然后我们使用 RAII 和模板元编程注入转换样板文件。
A ptr_filler
是一种 RAII 类型,它将 unique_ptr<T>*
转换为 T**
:
template<class T>
struct ptr_filler {
std::unique_ptr<T>* output = nullptr;
T* temporary = nullptr;
ptr_filler( std::unique_ptr<T>* bind ):output(bind) {}
operator T**()&&{return &temporary;}
~ptr_filler() {
if (temporary)
*output = std::unique_ptr<T>(temporary);
}
};
ret_converter_t
执行从 C 风格 API 到 C++ unique-ptr API:
的类型转换
template<class T> struct ret_converter { using type=T; };
template<class T> using ret_converter_t = typename ret_converter<T>::type;
template<class T> struct ret_converter<T*> { using type=std::unique_ptr<T>; };
get_converter_t
将参数类型从 C 风格 API 转换为通过 ptr_filler
:
填充唯一指针的类型
template<class T> struct get_converter { using type=T; };
template<class T> using get_converter_t = typename get_converter<T>::type;
template<class T> struct get_converter<T**> { using type=ptr_filler<T>; };
最后,call
从你传递给它的函数指针推导出它的参数,然后转换参数和retval以使用唯一的ptr内存管理,并为你调用函数f
:
template<class R, class...Args>
ret_converter_t<R> call( R(* f)(Args...), get_converter_t<Args>... args ) {
return static_cast<ret_converter_t<R>>( f( std::forward<decltype(args)>(args)... ) );
}
现在我们可以:
struct CustomType {
int x;
};
CustomType* GetTheObject(int x) { return new CustomType{x}; }
bool MakeTheObject( CustomType** pp, int a, int b ) { *pp = new CustomType{a+b}; return a>b; }
我们可以做到:
int main() {
std::unique_ptr<CustomType> ptr;
std::cout << call( MakeTheObject, &ptr, 2, 1 ) << " = 1\n";
std::cout << ptr->x << " = 3\n";
ptr = call( GetTheObject, 7 );
std::cout << ptr->x << " = 7\n";
}
您可以使用 call<MakeTheObject>
语法变得更漂亮,但这需要工作。这假设您正在包装的 API 是 C-ish API 但 returns new
对象。
通常,当创建指向对象的指针并从函数返回时,您使用 unique_ptr 来确保它在范围末尾被删除。
CustomType* GetTheObject(); // Let's say this the function signature and it returns a new object
void Main() {
auto object = make_unique(GetTheObject());
object->DoSomething();
// Then the object is deleted automatically
}
如果函数签名是这样的怎么办
bool GetTheObject(CustomType** object);
我可以想象一种相当冗长的消费方式
void Main() {
// Declare a pointer which we never need
CustomType* object_ptr;
if(GetTheObject(&object_ptr)) {
// Then create a unique_ptr out of it
auto object = make_unique(object_ptr);
object->DoSomething();
// Then the object is deleted automatically
}
}
在这种情况下,是否有更好的推荐方法来使用对象。我可以考虑另一个实现 & 运算符的 unique_ptr2 class 然后像
一样使用它unique_ptr2 object;
if(GetTheObject(&object)) {
// use it
}
是否有现成可用的 unique_ptr2 实现可以做到这一点?感觉还是不太理想。有没有更好的方法?
我认为返回 std::unique_ptr
比返回原始指针更安全,因为返回原始指针有调用代码意外泄漏对象的风险。我建议这样做:
#include <iostream>
#include <memory>
class CustomType
{
// ...
};
std::unique_ptr<CustomType> GetTheObject()
{
if ((rand()%2) != 0) return std::make_unique<CustomType>();
return std::unique_ptr<CustomType>(); // nothing to return, sorry
}
int main(int argc, char ** argv)
{
if (std::unique_ptr<CustomType> p = GetTheObject())
{
std::cout << "Got the object!" << std::endl;
}
return 0;
}
如果您不得不忍受一个您不喜欢其形状且无法更改的现有函数,您可以将丑陋隐藏在包装函数中,然后改为调用包装函数:
std::unique_ptr<CustomType> PrettyGetTheObject()
{
CustomObject * obj;
if (GetTheObject(&obj)) return std::unique_ptr<CustomObject>(obj);
return std::unique_ptr<CustomType>(); // nothing to return, sorry
}
一种可能的方法是使用包装函数。示例:
bool GetOriginal(char **pptr);
bool GetWrapped(std::unique_ptr<char> * puptr) {
bool result;
if (puptr != nullptr) {
char * cptr;
result = GetOriginal(&cptr);
*puptr = std::make_unique(cptr);
} else {
result = GetOriginal(nullptr);
}
return result;
}
这假定了传递 null 的常用模式以避免获取管理指针(例如,如果您只对 return 值感兴趣)。
如果将 null 传递给原始函数不是其 api 的一部分,那么您当然可以使用对 std::unique_ptr
的引用而不是原始指针。
如果你有很多这样的函数,你当然也可以为此编写一个通用的包装函数:
template<typename Fn, typename T>
bool wrap(Fn fn, std::unique_ptr<T> * puptr) {
bool result;
if (puptr != nullptr) {
T * cptr = nullptr;
result = fn(&cptr);
*puptr = std::make_unique(cptr);
} else {
result = fn(nullptr);
}
return result;
}
// usage: wrap(GetOriginal, &some_unique_ptr)
如果原始函数需要更多参数,则使用 std::bind
或 lambda。
我可能想编写自动转换 to/from 唯一指针的代码。我们从具有相同签名的现有函数 "automatically" 生成一个新函数,但是 T*
return 值是 unique_ptr<T>
并且 T**
参数是 unique_ptr<T>*
参数。
然后我们使用 RAII 和模板元编程注入转换样板文件。
A ptr_filler
是一种 RAII 类型,它将 unique_ptr<T>*
转换为 T**
:
template<class T>
struct ptr_filler {
std::unique_ptr<T>* output = nullptr;
T* temporary = nullptr;
ptr_filler( std::unique_ptr<T>* bind ):output(bind) {}
operator T**()&&{return &temporary;}
~ptr_filler() {
if (temporary)
*output = std::unique_ptr<T>(temporary);
}
};
ret_converter_t
执行从 C 风格 API 到 C++ unique-ptr API:
template<class T> struct ret_converter { using type=T; };
template<class T> using ret_converter_t = typename ret_converter<T>::type;
template<class T> struct ret_converter<T*> { using type=std::unique_ptr<T>; };
get_converter_t
将参数类型从 C 风格 API 转换为通过 ptr_filler
:
template<class T> struct get_converter { using type=T; };
template<class T> using get_converter_t = typename get_converter<T>::type;
template<class T> struct get_converter<T**> { using type=ptr_filler<T>; };
最后,call
从你传递给它的函数指针推导出它的参数,然后转换参数和retval以使用唯一的ptr内存管理,并为你调用函数f
:
template<class R, class...Args>
ret_converter_t<R> call( R(* f)(Args...), get_converter_t<Args>... args ) {
return static_cast<ret_converter_t<R>>( f( std::forward<decltype(args)>(args)... ) );
}
现在我们可以:
struct CustomType {
int x;
};
CustomType* GetTheObject(int x) { return new CustomType{x}; }
bool MakeTheObject( CustomType** pp, int a, int b ) { *pp = new CustomType{a+b}; return a>b; }
我们可以做到:
int main() {
std::unique_ptr<CustomType> ptr;
std::cout << call( MakeTheObject, &ptr, 2, 1 ) << " = 1\n";
std::cout << ptr->x << " = 3\n";
ptr = call( GetTheObject, 7 );
std::cout << ptr->x << " = 7\n";
}
您可以使用 call<MakeTheObject>
语法变得更漂亮,但这需要工作。这假设您正在包装的 API 是 C-ish API 但 returns new
对象。