我正在研究一个C库,其中一部分涉及一些数学类型并对它们进行操作。每种类型都有一个工厂构造函数/析构函数,可以动态分配和释放它们。例如:
/* Example type, but illustrates situation very well. */
typdef struct {
float x;
float y;
float z;
} Vector3D;
/* Constructor */
Vector* Vector3D_new(float x, float y, float z) {
Vector3D* vector = (Vector3D*) malloc(sizeof(Vector3D));
/* Initialization code here...*/
return vector;
}
/* Destructor */
void Vector3D_destroy(Vector3D* vector) {
free(vector);
}
尼斯&简单,并且还减轻了用户正确初始化的负担。
现在我主要关注的是如何处理对这些类型进行操作的函数(特别是如何返回结果值。)几乎每个二进制操作都会产生一个相同类型的新实例,因此,我需要考虑如何把这个还给用户。我可以通过 value 返回内容,但是传递指针会更受欢迎,因为它更快,与构造/析构函数方法兼容,并且不会给用户带来太多负担。
目前我通过让函数动态分配结果,然后返回指向它的指针来实现它:
/* Perform an operation, and dynamically return resultant vector */
Vector3D* addVectors(Vector3D* a, Vector3D* b) {
Vector3D* c = Vector3D_new(
a->x + b->x,
a->y + b->y,
a->z + b->z);
return c;
}
通过将值直接返回给用户,它具有能够链接的优点(例如,作为参数直接传递到另一个函数),例如:
/* Given three Vector3D*s : a, b, & c */
float dot = dotProduct(crossProduct(a, addVectors(b, c));
但是考虑到当前的方法,这会导致内存泄漏,因为addVectors()
的结果将直接传递给crossProduct()
,并且用户将没有机会{{1它(和传递到free()
的{{1}}结果相同)。为了使这个工作,一个人必须创建一个指针来保存值,使用它,然后通过所述指针crossProduct()
。
dotProduct()
这样可行,但不那么直观,并且失去了我所希望的链接效果。
另一种可能性是让操作函数有3个参数;两个用于操作数,一个用于存储结果,但同样不是很直观。
我的问题是:什么是优雅& 高效在二进制操作中使用动态内存的方法?作为奖励,已经在现实世界图书馆中使用的解决方案将非常酷。有任何想法吗? :)
答案 0 :(得分:2)
除了您提到的内存泄漏之外,您当前系统还存在一些其他问题:
- 分配到堆显着比普通堆栈操作慢。
- 每个分配也需要
free()
d,这意味着每个实例都需要至少 2 函数调用,其中只使用基于堆栈的设计将不需要。- 由于必须手动管理内存,因此会留下更多内存泄漏的空间。
- 内存分配可能会失败!基于堆栈的系统可以减轻这种情况。
- 使用指针需要解除引用。这比直接访问慢,并且需要更多(也许是草率)sytax。
除此之外,许多编译器缓存用于程序堆栈的内存,并且可以在堆上提供显着的改进(几乎从不缓存(如果可能的话!))
简而言之,只需依靠堆栈就可以获得最佳性能,不仅仅是性能,还有维护和清洁代码。唯一要记住的是堆栈是有限的,它也可能很容易变得疯狂。将堆栈用于短期数据(在这种情况下为二进制运算结果),堆栈用于较长期的数据。
我希望这有帮助! :)
注意:此答案中的大部分信息都归功于@ Justin。
答案 1 :(得分:1)
在操作员内部分配并不像看起来那么方便 这主要是因为您没有垃圾收集,也因为您不得不担心分配失败。
考虑以下代码:
Vector3D *v1,*v2,*v3;
Vector3d v4 = addVectors(v1,multiplyVectors(v2,v3));
看起来不错。
但是从multiplyVectors
返回的向量会发生什么?内存泄漏。
如果分配失败会发生什么?其他一些功能崩溃。
我会在原地加入:
void addVectors(Vector3D *target, const Vector3D *src);
这相当于target += src;
。
答案 2 :(得分:1)
我会像
一样简单Vector3D addVectors(Vector3D a, Vector3D b) {
Vector3D c;
c.x = a.x + b.x;
c.y = a.y + b.y;
c.z = a.z + b.z;
return c;
}
如果调用者真的需要它,他可以自己复制它。