返回指向函数声明的数据的指针
我知道这不起作用,因为变量x在函数返回时被销毁:
int* myFunction() { int x = 4; return &x; }
那么如何正确地返回指向我在函数中创建的东西的指针,以及我需要注意什么? 如何避免内存泄漏?
我也用过malloc:
int* myFunction2() { int* x = (int*)malloc(sizeof int); *x = 4; return x; }
你是如何正确地做到这一点的 – 在C和C ++中?
对于C ++,您可以使用智能指针来强制执行所有权转移。 auto_ptr
或boost::shared_ptr
是不错的选择。
你的第二种方法是正确的。 您只需要清楚地记录调用者“拥有”结果指针,并负责释放它。
由于这种额外的复杂性,很少为像“int”这样的“小”类型执行此操作,尽管我假设您在这里仅使用了一个int作为示例。
有些人还希望将指向已分配对象的指针作为参数,而不是在内部分配对象。 这使得更清楚的是调用者负责释放对象(因为他们首先分配它),但是使调用站点更加冗长,所以这是一个权衡。
对于C ++,在许多情况下,只需按值返回。 即使在较大物体的情况下, RVO也经常避免不必要的复制。
一种可能性是将函数传递给指针:
void computeFoo(int *dest) { *dest = 4; }
这很好,因为你可以使用自动变量这样的函数:
int foo; computeFoo(&foo);
使用这种方法,您还可以将内存管理保留在代码的相同部分,即。 你不能错过malloc只是因为它发生在函数内部的某个地方:
// Compare this: int *foo = malloc(…); computeFoo(foo); free(foo); // With the following: int *foo = computeFoo(); free(foo);
在第二种情况下,你更容易忘记免费,因为你没有看到malloc。 这通常至少部分通过约定来解决,例如:“如果函数名称以XY开头,则表示您拥有它返回的数据。”
返回指向“function”变量的指针的有趣角落情况是声明变量static:
int* computeFoo() { static int foo = 4; return &foo; }
当然这对于正常编程来说是邪恶的,但它有朝一日可能会派上用场。
C ++方法可以避免内存泄漏。 (至少在忽略函数输出时)
std::auto_ptr myFunction() { std::auto_ptr result(new int(4)); return result; }
然后叫它:
std::auto_ptr myFunctionResult = myFunction();
编辑:正如乔尔所指出的那样。 std :: auto_ptr有它自己的缺点,通常应该避免。 而不是std :: auto_ptr你可以使用boost :: shared_ptr(std :: tr1 :: shared_ptr)。
boost::shared_ptr myFunction() { boost::shared_ptr result(new int(5)); return result; }
或者当使用C ++ 0x符合编译器时您可以使用std :: unique_ptr。
std::tr1::unique_ptr myFunction() { std::tr1::unique_ptr result(new int(5)); return result; }
主要区别在于:
-
shared_ptr允许多个shared_ptr实例指向同一个RAW指针。 它使用引用计数机制来确保只要存在至少一个shared_ptr实例就不会释放内存。
-
unique_ptr只允许它的一个实例持有指针,但与auto_ptr不同,它具有真正的移动语义。
在C ++中,您应该使用new
:
int * myFunction() { int blah = 4; return new int(blah); }
要摆脱它,使用删除:
int main(void) { int * myInt = myFunction(); // 做东西 删除myInt; }
请注意,我在使用new
调用int的复制构造函数,以便将值“4”复制到堆内存中。 获取指向堆栈上某些东西的指针的唯一方法是通过正确调用new
将其复制到堆上。
编辑:如另一个答案中所述,您还需要记录稍后调用者需要释放指针。 否则您可能会发生内存泄漏。
还有另一种方法 – 声明x
静态。 在这种情况下,它将位于数据段中,而不是位于堆栈上,因此在程序运行时期间它是可用的(并且是持久的)。
int *myFunction(void) { static int x = 4; return &x; }
请注意,只有在第一次调用myFunction
才会执行赋值x=4
:
int *foo = myFunction(); // foo is 4 *foo = 10; // foo is 10 *foo = myFunction(); // foo is 10
NB! 使用函数范围静态变量不是踏板安全技术。
您的第二个代码段是正确的。
为了帮助避免内存泄漏,我让编码约定帮助我。
xxxCreate()将为xxx分配内存并初始化它。 xxxDelete()将破坏/损坏xxx并释放它。
xxxInit()将初始化xxx(永不分配)xxxDestroy()将破坏/损坏xxx(永不免费)
另外,一旦我将代码添加到create / init / malloc,我就会尝试添加代码来删除/销毁/释放。 它并不完美,但我发现它可以帮助我区分需要释放的物品和不需要物品的物品,以及减少我以后忘记释放物品的可能性。
Boost或TR1共享指针通常是要走的路。 它避免了复制开销,并为您提供半自动删除。 所以你的function应该是这样的:
boost::shared_ptr myFunction2() { boost::shared_ptr x = new int; *x = 4; return x; }
另一种选择就是允许复制。 如果对象很小(比如这个),那也不错,或者你可以安排在return语句中创建对象。 如果在return语句中创建对象,编译器通常会优化副本。
我会尝试这样的事情:
int myFunction2b( int * px ) { if( px ) { *px = 4; return 1; } // Choice 1: Assert or Report Error // Choice 2: Allocate memory for x. Caller has to be written accordingly. // My choice is 1 assert( 0 && "Argument is NULL pointer" ); return 0; }
你问的是如何正确返回一个指针。 这是错误的问题,因为你应该做的是使用智能指针而不是原始指针。 scoped_ptr和shared_ptr(在boost和tr1中可用)是很好的指针(例如这里和这里 )
如果你需要原始指针(例如传递给C函数), get()方法将提供它。
如果你必须创建原始指针,例如作业,那么你可以在函数中使用malloc() (就像你做的那样)或new ,并希望你记得去除内存(分别通过free()和delete )或者,在一个稍微不太可能泄密的习语中,你可以用new创建指针,将它传递给一个函数,并在你完成它时用delete删除 。 但是,再次使用智能指针。