2015-03-21 3 views
2

Рассмотрим следующий пример:Как использовать конструкцию const C, когда суперструктура ссылается на неконстантную?

typedef struct Collection 
{ 
    ... 
} Collection; 

typedef struct Iterator 
{ 
    Collection* collection; 
} Iterator; 

итератора является предложение по сбору модифицирующие функции, следовательно, она имеет адрес неконстантного коллекции. Тем не менее, он также предлагает немодифицирующие функции, которые вполне законны для использования с коллекцией const.

void InitIterator(Iterator* iter, Collection* coll) 
{ 
    iter->collection = coll; 
} 

void SomeFunction(const Collection* coll) 
{ 
    // we want to use just non-modifying functions here, as the Collection is const 
    Iterator iter; 
    InitIterator(&iter, coll); // warning, call removes const qualifier 
} 

Я ищу решение в C. Я вижу некоторые варианты:

1. В начале, литой коллекции для не-const. Это, вероятно, не неопределенное поведение, потому что в конечном итоге объект не должен изменяться. Но это прическа, так как имеет объект const, и это требует неприятностей. Итератор должен стать широко используемым универсальным механизмом для работы с коллекциями. Отсутствие предупреждений компилятора, когда нужно изменить коллекцию const, действительно плохо.

2. Два типа итератора, один из которых является версией только для чтения с элементом const Collection *. Это усложняет использование, потенциально требует дублирования некоторых функций, возможно, снижает эффективность за счет перевода. Я действительно не хочу усложнять API и иметь две разные структуры вместе с двумя наборами функций.

3. Итератор, имеющий оба указателя, и Init, принимающий оба указателя на сборку.

typedef struct Iterator 
{ 
    const Collection* collectionReadable; // use this when reading 
    Collection* collectionWritable;  // use this when writing 
} Iterator; 

При наличии константной Коллекции, неконстантная аргумент должна стать NULL и в конечном счете пытается изменить коллекцию врежется (пытаясь указатель разыменования NULL), что хорошо (безопасно). У нас есть дополнительная стоимость хранения. Это неудобно, принимая два указателя одной коллекции.

Я также немного обеспокоен тем, что компилятор видит оба указателя в том же контексте, что означает, что если коллекция будет модифицирована с помощью записываемого указателя, компилятор должен понять, что указатель только для чтения может указывать на один и тот же объект и его необходимо перезагрузить, несмотря на наличие определителя константы. Это безопасно?

Какой из них вы бы выбрали и почему? Знаете ли вы какой-либо лучший подход к этой проблеме?

+0

Почему SomeFunction * имеет *, чтобы взять указатель на const? Просто позвольте пользователю передать объект non const. – reader

+0

@reader Существует множество случаев, когда функции «гарантируют», что они не будут изменять объект. Он обеспечивает оптимизацию и бесценен в многопоточном коде. – user2231

ответ

0

Да, безопасно иметь два указателя на один и тот же объект, один const и другой неконстантный. Константа просто означает, что объект не может быть изменен с помощью этого конкретного указателя, но его можно изменить с помощью других указателей, если есть такие указатели. Однако, хотя это безопасно, это не означает, что вы должны выбрать решение с двумя указателями.

Для примера кода, который прекрасно работает правильно, см:

int x = 5; 
const int *p = &x; 
printf("%d\n", *p); 
x = 7; 
printf("%d\n", *p); 

Теперь * р меняется, даже если р константный указатель.

Ваша цель состоит в том, чтобы избежать дублирования кода, поэтому вы хотите просто направить указатель const на указатель, не являющийся константой. Это безопасно, если вы не изменяете объект. В стандартной библиотеке C имеется множество аналогичных примеров.Например, strchr() принимает указатель const char в строку и затем возвращает указатель на символ, если строка не была const, и вы хотите изменить ее с помощью возвращаемого значения. Я бы выбрал решение, принятое в стандартной библиотеке C, т. Е. Приведение типов.

Вы по существу заметили, что определитель const в C не идеален. Есть много вариантов использования, когда вы сталкиваетесь с такими проблемами, и самый простой способ продвижения - это, как правило, признать, что он не идеален и делает приведение типов.

+0

Я не уверен, что приведение типов является лучшим решением из-за специфической детали, что Итератор должен стать внешним, широко используемым объектом. И я думаю, что просить всех пользователей нарушить их const-конструкцию плохо. В противном случае я согласен с вами - если использовать только внутри страны, где я могу контролировать использование, то, вероятно, будет лучше. Ваш ответ на самом деле заставил меня больше ориентироваться на третий вариант, но я все еще не определился. – user2231

Смежные вопросы