0

我目前正在解决的设计问题是迭代某个内存区域,并在每次这样的迭代中从该内存中检索客户感兴趣的一些元数据。我目前看到 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_viewclobbers 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复制到 struct 指向的对象,queue_element_view *因此 next 不会破坏数据。

缺点:

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

  2. 它使测试公共 api 更加复杂。

所以我有点困惑哪一个更可取/可读?可能还有另一种选择?

4

1 回答 1

3

备选方案一

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

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

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

这使客户不必了解需要清理的内容、如何清理以及不需要清理的细节。然后,他们可以随心所欲地保留数据,并在他们决定使用完数据后对其进行清理。调用next()将意味着它们不再是迭代的当前元素的数据是一个特性,而不是一个错误——如果客户想要这样做,为什么要干扰保留先前迭代的数据的客户呢?

备选方案二

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

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

总体

你的两个选择似乎奇怪地颠倒了。

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

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

无论如何,是的,调用者承担了某种责任。这是很自然的——没有免费的午餐。清楚地记录这些责任是什么,并尝试以一种感觉一致的方式设计整个 API,即用户承担了哪些责任、在什么情况下以及如何履行这些责任。

于 2019-05-10T16:34:20.727 回答