C++ 使用作为参数返回的指针的最佳实践是什么
通常,当从函数创建并返回指向对象的指针时,您使用unique_ptr使用该指针,以确保在作用域末尾将其删除C++ 使用作为参数返回的指针的最佳实践是什么,c++,std,C++,Std,通常,当从函数创建并返回指向对象的指针时,您使用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(); // The
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
}
}
在这种情况下,有没有更好的推荐方法来使用对象。我可以考虑另一个,比如说实现&operator的惟一的_ptr2类,然后像这样使用它
unique_ptr2 object;
if(GetTheObject(&object)) {
// use it
}
是否有一个现成的unique_ptr2实现,允许这样做?感觉还是不理想。有更好的方法吗?我认为返回std::unique\u 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
}
我认为返回std::unique\u 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;
}
如果原始函数接受更多参数,则使用std::bind或lambda。一种可能的方法是使用包装器函数。例如:
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;
}
如果原始函数接受更多参数,则使用std::bind或lambda。我可能会尝试编写自动转换到唯一ptr的代码。我们从现有函数自动生成一个具有相同签名的新函数,但T*返回值是唯一的,T**参数是唯一的 然后,我们使用RAII和模板元编程注入转换样板 ptr_filler是一种RAII类型,可将唯一的ptr*转换为T**: get_converter_t将参数类型从C样式API转换为通过ptr_填充符填充唯一ptr的类型: 现在我们可以:
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";
}
您可以更喜欢调用语法,但这需要工作。这假设您正在包装的API是C-ish API,但返回新的对象
。我们从现有函数自动生成一个具有相同签名的新函数,但T*返回值是唯一的,T**参数是唯一的 然后,我们使用RAII和模板元编程注入转换样板 ptr_filler是一种RAII类型,可将唯一的ptr*转换为T**: get_converter_t将参数类型从C样式API转换为通过ptr_填充符填充唯一ptr的类型: 现在我们可以:
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";
}
您可以更喜欢调用语法,但这需要工作。这假设您正在包装的API是C-ish API,但返回新的对象
.在现代代码中,我假设任何原始指针都是观察指针,不参与所有权。因此,不必消耗它。如果它是一个拥有指针的指针,它应该已经是一个UNIQUEYPTR或SydDypTr.C++中,main函数是main main,它必须总是有返回类型int。当然,我在讨论使用一个函数,它是用BooGETToeObjutCuffyTy**对象创建的,API函数,我无法控制它,我需要以最好的方式使用该函数的结果。关于主函数,请,这只是一个描述实际问题的示例,您可以称之为全局函数或其他任何函数。@Alex这就是原始指针的问题所在。您不能仅根据类型来假设它是否拥有。如果您信任代码库,则可以依赖约定。否则,您需要查找函数的文档或源代码以了解它是什么。如果您知道它是一个拥有的指针,那么最好的办法可能是立即将该所有权赋予一个独特的\u ptr,就像您在这里所做的那样。但是,因为它看起来可以修改GetTheObject,所以我会将它与返回值捆绑在一起。您关于消费的措辞是偶然的还是有意的?因为如果这是故意的,那么只有一个正确的解决方案,那就是你怎么做,看看你怎么解释你不控制对象。消费意味着所有权和责任的转移,所以唯一的ptr是正确的。共享的ptr应该是我不太知道谁拥有它,而原始指针通常被解释为与std::experimental::observer_ptr相似或相同,即不拥有
任何原始指针都是观察指针,不参与所有权。因此,不必消耗它。如果它是一个拥有指针的指针,它应该已经是一个UNIQUEYPTR或SydDypTr.C++中,main函数是main main,它必须总是有返回类型int。当然,我在讨论使用一个函数,它是用BooGETToeObjutCuffyTy**对象创建的,API函数,我无法控制它,我需要以最好的方式使用该函数的结果。关于主函数,请,这只是一个描述实际问题的示例,您可以称之为全局函数或其他任何函数。@Alex这就是原始指针的问题所在。您不能仅根据类型来假设它是否拥有。如果您信任代码库,则可以依赖约定。否则,您需要查找函数的文档或源代码以了解它是什么。如果您知道它是一个拥有的指针,那么最好的办法可能是立即将该所有权赋予一个独特的\u ptr,就像您在这里所做的那样。但是,因为它看起来可以修改GetTheObject,所以我会将它与返回值捆绑在一起。您关于消费的措辞是偶然的还是有意的?因为如果这是故意的,那么只有一个正确的解决方案,那就是你怎么做,看看你怎么解释你不控制对象。消费意味着所有权和责任的转移,所以唯一的ptr是正确的。共享的ptr应该是我不太清楚谁拥有它,而原始指针通常被解释为类似于std::experimental::observative\u ptr,或者与std::observative::observator\u ptr相同,即非拥有。我完全理解这一点。您正在更改问题的初始条件。我无法控制GetTheObject函数签名。这就是问题所在。我想找到一种在这些特殊条件下使用指针的最佳方法。@Alex没有通用的最佳方法。在处理遗留代码和可能拥有原始指针时,您只需阅读代码就可以看到发生了什么,并进行相应的调整。我完全理解这一点。您正在更改问题的初始条件。我无法控制GetTheObject函数签名。这就是问题所在。我想找到一种在这些特殊条件下使用指针的最佳方法。@Alex没有通用的最佳方法。当处理遗留代码并可能拥有原始指针时,您只需阅读代码即可了解情况并进行相应调整。您认为最好的方法是为每个函数创建一个包装器,将指针作为参数返回?在本例中,我可以从技术上创建一个方法签名,如另一个答案中建议的std::unique_ptr GetTheObjectWrap,但是如果有10个或30个函数,那么包装它们似乎太多了。我在考虑创建一个适配器或另一个unique_ptr的实现,或者我可以做一次并在任何地方使用它的东西。不冗长且不易使用的东西。这就是我对通用包装器的意思。。e、 g.对于类似于wrapGetOriginal的用法,您认为最好的方法是为每个返回指针作为参数的函数创建一个包装器?在本例中,我可以从技术上创建一个方法签名,如另一个答案中建议的std::unique_ptr GetTheObjectWrap,但是如果有10个或30个函数,那么包装它们似乎太多了。我在考虑创建一个适配器或另一个unique_ptr的实现,或者我可以做一次并在任何地方使用它的东西。不冗长且不易使用的东西。这就是我对通用包装器的意思。。e、 g.对于类似于wrapGetOriginal的用法,末尾的_unique _ptr非常有趣。非常感谢。我可能会尝试重构它,将其称为call,但总体而言,它看起来几乎是我想要的。非常有趣。非常感谢。我可能会尝试重构它,将其称为call,但总体而言,它看起来几乎符合我的要求。