c语言 - 提供其当前元素"view"的迭代器



我目前正在解决的设计问题是遍历内存的某个区域,并在每次此类迭代中从该内存中检索客户端感兴趣的一些元数据。我目前看到 2 种解决方案:

我。

struct queue;
struct queue_element_view{
int id;
char *description;
};
//0 - if ok, -1 - if end of queue reached
int next(struct queue*);
//0 - if ok, -1 - if end of queue reached    
int current_element_view(struct queue*, struct queue_element_view *);

因此,队列不透明结构可以通过next函数遍历,并且由于队列元素依赖于平台,并且我想保持库跨平台,我提供了一个独立于平台的struct queue_element_view,在所有平台上都是合理的。

缺点:

如果客户端编写如下代码:

struct queue *queue_ptr = //
struct queue_element_view current_out;
current_element_view(queue_ptr, &current_out);
//current_out now contains current's element meta data
next(queue_ptr);
//current_out now may contain unspecified data
//since queue's current element changed.

所以打电话给next后打电话给current_element_view破坏了current_out.

第二。

struct queue;
struct queue_element_view;
struct queue_element_view *allocate_view(void); 
int * get_id(struct queue_element_view *);
char * get_description(struct queue_element_view *);
//0 - if ok, -1 - if end of queue reached
int next(struct queue*);
//0 - if ok, -1 - if end of queue reached    
int current_element_view(struct queue*, struct queue_element_view *);

在这种情况下,我们已经分配了struct queue_element_viewcurrent_element_view将数据复制到结构queue_element_view *指向的对象,因此接下来不会破坏数据。

缺点:

  1. 它涉及一个函数附加调用,以简单地检索intchar *字段

  2. 它使测试公共 API 变得更加复杂。

所以我有点困惑哪一个是可推荐/可读的?可能还有另一种选择?

备选方案 I

备选方案 (I) 的感知问题显然是,由于next()(可能)解除分配内存,调用next()将导致先前复制到struct queue_element_view中的数据变得无效。

解决方案 :确保next()不会这样做。 这可能意味着您必须创建描述字符串的副本以放入视图中,而不仅仅是向客户端提供原始指针本身的副本。 在这种情况下,提供一个函数来释放struct queue_element_view中反映的任何内部分配可能会有所帮助,也许是这样的:

void queue_element_view_clean(struct queue_element_view *view) {
free(view->description);
}

这使客户不必知道需要清理的内容、清理方式和不需要清理的详细信息。 然后,他们可以根据需要保留数据,并在决定完成数据时对其进行清理。 调用next()将意味着它们不再是迭代当前元素的数据,这是一个功能,而不是一个错误 - 如果他们想要这样做,为什么干扰客户端保留以前迭代的数据是有意义的?

备选案文二

感知到的问题围绕着通过函数访问视图成员。 目前还不清楚这如何解决替代方案I的感知问题。 虽然它可以成为该问题解决方案的一部分,但我认为没有理由认为它是必要的部分。

解决方案:使用备选方案 I。 认真地。 如果您要根据需要制作数据副本以使视图在调用时适当地保留next(),那么我不明白您如何通过使视图结构不透明来获得任何好处。

整体

你的两个选择似乎奇怪地翻转了。

如果您想避免使用单独的视图结构或复制数据,那么使用访问器函数对我来说是有意义的。 这些函数将返回与迭代的当前元素相关的数据 - 不涉及单独的视图结构。 然后,您可以选择让访问者提供调用方负责的数据的副本,或者使调用方负责在迭代器向前推进时复制他们想要保留的任何数据。

另一方面,如果您为元素视图提供单独的结构,那么这样做的方式似乎很奇怪,因为它在迭代器前进时变得无效。 单独的视图对象似乎是一种自然的方法,只要调用方需要,就可以保留视图数据。

无论如何,是的,某种责任被放在呼叫者身上。 这是很自然的——没有免费的午餐。 清楚地记录这些责任是什么,并尝试以与用户承担何种责任、在什么情况下以及如何履行这些责任相一致的方式设计整体 API。

最新更新