![](/img/trans.png)
[英]What is the best way to return an image or video file from a function using c++?
[英]What is the best way to return an object from a function (C)
这在某种程度上是一个主观问题,但似乎应该有一个标准。 我正在制作树状数据结构,我想知道从函数传递新节点的最佳方法。 我有几个想法,但我不知道哪个是最安全/最有效的。
这是我的代码的简化:
typedef struct Node {
struct Node *left;
struct Node *right;
int value;
} Node;
int f() {
//do stuff
}
Node *new_node() {
Node n = {NULL, NULL, f()};
return &n;
}
int main() {
Node a = {new_node(), new_node(), 0};
}
显然,这不起作用,因为new_node()
函数返回的指针指向堆栈分配的数据,这些数据将在new_node()
结束后立即释放。 但是解决这个问题的最佳方法是什么?
一种可能性是在堆上分配n
,如下所示:
Node *new_node() {
Node *n = (Node *) malloc(sizeof(Node)); //unsure if the cast is necessary here, but that's not relevant
n->left = NULL;
n->right = NULL;
n->value = f();
return n;
}
但这感觉不对,因为它要求调用者函数直接处理内存清除,这可能会很快变得混乱。
我见过的另一个选项(特别是当对象是数组或缓冲区而不是对象时)是传递指向函数的指针,只是修改指针的内容。
void new_node(Node *n) {
n->left = NULL;
n->right = NULL;
n->value = f();
}
int main() {
Node n = {NULL, NULL, 0};
Node n1 = n;
new_node(&n);
new_node(&n1);
Node a = {&n, &n1, 0};
}
或者你可以直接传递数据,特别是因为在这种情况下Node
非常小:
Node new_node() {
Node n = {NULL, NULL, f()}
return n;
}
这似乎会慢一点,虽然我不确定。
关于这个主题已经有了一些答案,但它们都是用C ++编写的,并且处理引用和指针,我恐怕不太了解它们之间的区别。
这样做的标准方法是什么?
(对不起,如果这太长了)
您涵盖了所有四种可用方法,包括一种无效方法。 在他们之间挑选取决于您的设计偏好:
既然你问过, 我是否投出了malloc的结果? 没有。
至于你关注的问题,所有方法都有它们的优点和缺点,所以选择一个用于你的项目并保持对该决定的承诺(不要对一个函数使用一种方法,然后对树的另一个函数使用另一种方法)。
所有权可以是任何一个函数(但调用者必须调用将取消分配树的那个),或者调用者自己。
例如,在库中,我希望它们处理数据结构的内存分配,然后我只需调用库本身提供的函数来解除分配空间。 这样我就不会进入他们的内部细节,这很好。
通常我赋予函数所有权,但这是主观的。
这里的基本决定是谁控制记忆。 你是否让呼叫者决定并将更多的工作转移给他们? 或者你为他们处理?
为什么不兼得? 从OO构造函数模式中获取页面,将创建与初始化分开。
void *Node_init( Node *node ) {
node->left = NULL;
node->right = NULL;
node->value = 0;
}
Node *Node_new() {
Node *node = malloc(sizeof(Node));
Node_init(node);
return node;
}
void *Node_free( Node *node ) {
free(node);
}
请注意,我没有硬编码任何类型的函数调用初始化node->value
因为这会限制这些Node函数的灵活性,它只是设置为0以确保它没有填充垃圾。 并且因为调用者编写node->value = ...
实际上是微不足道的。
现在你可以做到这两点。
int main(void) {
Node my_node;
Node_init(my_node);
Node *your_node = Node_new();
// Use them as you like
Node_free(your_node);
}
然而! 谁控制内存的问题可能导致问题,如此连接的树结构。 如果你想破坏整个树,你将不得不抓取它并销毁所有节点。 但是,如果您不知道谁拥有节点的内存,那么清理将很困难。
因此,对于树或图形,您应该通过仅允许一个内存模式来简化操作。 堆内存更通用,特别是在大型程序中,所以我会选择它。
如果您有已知数量的节点,则不需要链接列表。 这意味着您需要在实际场景中动态分配节点。 因此,第一种方法需要较少的样板代码。
Node* node = new_node();
if (node == NULL)
goto error;
但是,这种方法迫使一个人使用malloc
而不是另一个分配器。 [1]第二种方法提供了更大的灵活性[2] ,所需的额外代码相当小。 采用第二种方法使图书馆更具可重用性。
Node* node = malloc(sizeof(Node));
if (node == NULL)
goto error;
init_node(node);
我认为第三种方法没有任何优势。
例如,Perl扩展应该使用Perl的内存分配器而不是malloc
。 例如,静态分配的内存优于嵌入式系统中动态分配的内存。
它可以与静态和动态分配的结构一起使用,您可以使用除malloc
之外的分配器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.