2017-02-03 138 views
3

我如何在C对象方法在C:内存泄漏

正确地实现对象的问题是,我从方法更容易出现内存泄漏返回对象其实不是,例如,从来没有返回一个对象,在参数列表中通过引用来做这件事?

extern void quaternion_get_product(Quaternion * this, Quaternion * q, Quaternion * result); 

这样的malloc()通话仅在构造函数中完成,所以它更容易控制。

我是新来的这种封装在C中,所以我不知道这是否会解决我的问题的方式。我只想让我的代码具有可扩展性,并且我发现如果我继续这样做,内存泄漏将会遍布全球,并且调试将非常困难。它通常如何接近?我的代码是否正确?

我的问题是,如果我有这样的:

Quaternion p = *quaternion_create(1, 0, 0, 0); 
Quaternion q = *quaternion_create(1, 0, 1, 0); 
Quaternion r = *quaternion_create(1, 1, 1, 0); 
Quaternion s = *quaternion_create(1, 1, 1, 1); 

p = *quaterion_get_product(&p, &q); // Memory leak, old p memory block is not being pointed by anyone 

Quaternion t = *quaternion_get_product(&q, quaternion_get_product(&s, &r)); 

内存泄漏是存在于由任何现有的指针嵌套函数调用时,中间存储器块不被指出,不能调用quaternion_destroy

头文件:

#ifndef __QUATERNIONS_H_ 
#define __QUATERNIONS_H_ 

#include <stdlib.h> 

typedef struct Quaternion Quaternion; 

struct Quaternion { 
    float w; 
    float x; 
    float y; 
    float z; 
}; 

extern Quaternion *quaternion_create(float nw, float nx, float ny, float nz); 
extern void quaternion_destroy(Quaternion *q); 
extern Quaternion *quaternion_get_product(Quaternion *this, Quaternion *q); 
extern Quaternion *quaternion_get_conjugate(Quaternion *this); 
extern float quaternion_get_magnitude(Quaternion *this); 
extern void quaternion_normalize(Quaternion *this); 
extern Quaternion *quaternion_get_normalized(Quaternion *this); 
#endif 

实现文件:

#include "quaternion.h" 
#include <math.h> 

Quaternion *quaternion_create(float nw, float nx, float ny, float nz) { 
    Quaternion *q = malloc(sizeof(Quaternion)); 

    q->w = nw; 
    q->x = nx; 
    q->y = ny; 
    q->z = nz; 
    return q; 
} 

void quaternion_destroy(Quaternion *q) { 
    free(q); 
} 

Quaternion *quaternion_get_product(Quaternion *this, Quaternion *p) { 
     Quaternion *return_q = quaternion_create(
      this->w * p->w - this->x * p->x - this->y * p->y - this->z * p->z, // new w 
      this->w * p->x + this->x * p->w + this->y * p->z - this->z * p->y, // new x 
      this->w * p->y - this->x * p->z + this->y * p->w + this->z * p->x, // new y 
      this->w * p->z + this->x * p->y - this->y * p->x + this->z * p->w 
     ); 
     return return_q; 
} 

Quaternion *quaternion_get_conjugate(Quaternion *this) 
{ 
     return quaternion_create(this->w, -this->x, -this->y, -this->z); 
} 

float quaternion_get_magnitude(Quaternion *this) { 
     return sqrt(this->w * this->w + this->x * this->x + this->y * this->y + this->z * this->z); 
} 

void quaternion_normalize(Quaternion *this) { 
     float m = quaternion_get_magnitude(this); 
     this->w /= m; 
     this->x /= m; 
     this->y /= m; 
     this->z /= m; 
} 

Quaternion *quaternion_get_normalized(Quaternion *this) { 
     Quaternion *r = quaternion_create(this->w, this->x, this->y, this->z); 
     quaternion_normalize(r); 
     return r; 
} 
+0

有你想仿效C对象的理由? C++会不会更容易? –

+0

我根本不使用malloc/free,主要是因为性能较低,因为malloc/free是相当昂贵的操作。 –

+1

只是一面评论。我怀疑你正在实现四元数来对性能敏感的东西进行数学运算。不断的malloc会绝对破坏你的表现。如何避免内存泄漏并且不会同时破坏您的性能的一个体面的答案是:不要malloc。不惜一切代价避免malloc,让用户处理分配和释放。 – Art

回答

3

实际上,它可以变得更糟,如果一些功能有更新的东西都副作用,并返回新构造的值。说,谁记得scanf返回一个值?

看着GNU Multi Precision Arithmetic Library我建议以下是一个合理的解决方案(实际上,他们甚至走得更远通过使内存分配用户的头疼,不是库的):

  1. 只有一个构造函数可以创建新的对象。我还建议所有构造函数的名称遵循相同的模式。
  2. 只有析构函数可以销毁已经存在的对象。
  3. 功能需要两个输入参数(比如,中+两侧)和输出参数(S)(放在哪里的结果,压倒一切,这是在以前的对象的一切),是这样的:

mpz_add (a, a, b); /* a=a+b */

通过这种方式,您将始终清楚地看到对象何时被创建/销毁,并且可以确保没有泄漏或双重空闲。当然,这可以防止您将多个操作“链接”在一起,并使您可以手动管理临时变量以获得中间结果。但是,我相信你仍然需要在C中手动执行该操作,因为即使编译器对动态分配的变量的生命周期知之甚少。

其实,如果我们不留下。 3并添加“库不管理内存”子句,我们将得到更加容易出错的解决方案(从库的角度来看),这将需要库的用户管理他们想要的内存。这样你也不会锁定内存分配的用户,这是一件好事。

1

没有必要动态分配你的quaterions。四元数的大小是固定的,因此您只能使用纯四元数。如果您进行整数计算,您也可以使用int s,而不必为每个int动态分配空间。

思路不使用malloc/free(未测试的代码)

Quaternion quaternion_create(float nw, float nx, float ny, float nz) { 
    Quaternion q; 

    q.w = nw; 
    q.x = nx; 
    q.y = ny; 
    q.z = nz; 
    return q; 
} 

Quaternion quaternion_get_product(Quaternion *this, Quaternion *p) { 
    Quaternion return_q = quaternion_create(
     this->w * p->w - this->x * p->x - this->y * p->y - this->z * p->z, // new w 
     this->w * p->x + this->x * p->w + this->y * p->z - this->z * p->y, // new x 
     this->w * p->y - this->x * p->z + this->y * p->w + this->z * p->x, // new y 
     this->w * p->z + this->x * p->y - this->y * p->x + this->z * p->w 
    ); 
    return return_q; 
} 

用法

Quaternion p = quaternion_create(1, 0, 0, 0); 
Quaternion q = quaternion_create(1, 0, 1, 0); 
Quaternion r = quaternion_create(1, 1, 1, 0); 
Quaternion s = quaternion_create(1, 1, 1, 1); 

p = quaterion_get_product(&p, &q); 

Quaternion t = quaternion_get_product(&q, quaternion_get_product(&s, &r)); 

没有malloc小号也不free太没有可能的内存泄漏和性能会更好。

+0

这是一个好方法! – clearlight

+0

哇,这是我没有想到的......我知道这是一种优雅的方式。现在我看不出任何潜在的问题,这可能会增加可扩展性,你们中的任何一个都会看到任何问题? – Angel

+0

析构函数?...也许它只有当四元数结构的成员之一是指向某个数组或结构的指针时才会有代码? – Angel

1

在我看来,你说错了。

您还需要检查malloc()是否返回NULL并处理该失败(例如通过显示内存不足错误消息并在失败时退出,因为它通常是不可恢复的)。

UPDATE:

,因为你需要释放你必须采取更多行动,以保持指针的中间结果。

更新2

@MichaelWalz做法是巨大的。如果你不需要,为什么要处理所有的分配和指针管理?但是,如果您使用指针并分配内存,您必须保留指针,并确保您释放事物并将它们传递给/仔细重用它们。我已更新我的示例以处理嵌套的调用。

更新3

我,因为你正在传递指针,这已经是你想点什么地址从函数调用的参数删除&的。您没有正确分配功能的输出。注意固定示例中的指针分配方式也是如此不同。


Quaternion p = *quaternion_create(1, 0, 0, 0); 
Quaternion q = *quaternion_create(1, 0, 1, 0); 
Quaternion r = *quaternion_create(1, 1, 1, 0); 
Quaternion s = *quaternion_create(1, 1, 1, 1); 

p = *quaterion_get_product(&p, &q); // Memory leak, old p memory block is not being pointed by anyone 

Quaternion t = *quaternion_get_product(&q, quaternion_get_product(&s, &r)); 

Quaternion *p = quaternion_create(1, 0, 0, 0); 
Quaternion *q = quaternion_create(1, 0, 1, 0); 
Quaternion *r = quaternion_create(1, 1, 1, 0); 
Quaternion *s = quaternion_create(1, 1, 1, 1); 


Quaternian *tmp = quaterion_get_product(p, q); 
quaternian_destroy(p); 
p = tmp; 
tmp = quaternion_get_product(s, r) 
Quaternion *t = quaternion_get_product(q, tmp); 
quaternian_destroy(tmp); 
+0

唉。 ..带了我一些尝试,但我认为这个例子根据你的使用范例做了更多你想要的东西。 – clearlight