2010-10-14 2 views
9

Задавая этот вопрос, потому что я чувствую, что переменные-члены моей базы понадобятся позже в производных классах. Есть ли недостаток в их защите?Правильно ли использовать переменные-члены?

EDIT: Отредактировано, чтобы лучше показать мое намерение.

EDIT: @sbi: Это также неправильно?

Этот класс будет использоваться для записи и извлечения ошибок в других классах. Лучше ли извлечь из нее или использовать объект из этого - я не знаю. Но я думаю, что методы getter и setter - это то, о чем этот класс.

class ErrorLogger 
{ 
    public: 
     //Making this function virtual is optional 
     virtual void SetError(const char*, ...); 
     const char* GetError() const; 
    protected: 
     char* z_ErrorBuf; 
}; 
+1

Почему, по вашему мнению, переменные-члены будут необходимы позже в производных классах? Для чего они нужны, и почему они должны быть выставлены? –

ответ

21

Инкапсуляция является одной из основных особенностей ОО. Инкапсуляция ваших данных в классы означает, что пользователи класса не могут разбить инварианты данных класса, поскольку состояние класса может управляться только через его функции-члены.

Если вы позволяете производные классы доступ к их базовому классу данным, то производных классов должны заботиться, чтобы не аннулирует базовый класс инварианты Дейты. Это выдает инкапсуляцию из окна и просто неверно. (Так делают getters and setters, BTW.)

Я нахожусь с protected все меньше и меньше на протяжении многих лет, даже для функций члена. Если класс полностью реализует простую концепцию, то все его состояние должно быть манипулировано через его публичный интерфейс. Если производным классам нужны «задние двери», чтобы проникнуть внутрь, тогда я обычно задаю вопрос о моем дизайне.(Это не значит, что я никогда не использую protected. Я просто нахожу, что мне это нужно все меньше и меньше.)

+0

И в любом случае вы всегда должны создавать переменные как частные и использовать защищенные Свойства/Аксесуары. –

+2

@SoMoS: Посмотрите на связанный документ относительно сеттеров и геттеров. Это мерзость. – sbi

+1

@SoMoS: Я не согласен с 107%. Архитектура, которая использует простые геттеры и сеттеры для доступа к переменной, является глупой. Просто сделайте проклятые вещи публичными. Какая разница? –

2

Мне любопытно, что другие люди ответят на это.

Как и для парадигмы доступа, вы можете объявить их частными и использовать защищенные методы Getter и Setter. Если ваша реализация базового класса изменяется, вы можете изменить только те, кто использует этот метод.

2

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

Нет недостатка, если выполнено вышеупомянутое условие.

Если вы хотите сделать дополнительный шаг и защитить переменные от внешнего доступа, вы всегда можете создавать частные переменные-члены, а затем использовать защищенные методы для доступа/изменения этих частных переменных.

2

Наследование - это еще один способ использования класса. Это более сдержанно, и, заставляя ваши переменные-члены защищаться, вы паритесь еще больше, поэтому вы не должны изменять их, не зная о влиянии на каждый унаследованный класс. Если это ваш базовый класс и ваши унаследованные классы, то, вероятно, не будет никакого вреда, но вы потеряете контроль над доступом к членам (блокировка, ведение журнала, чтение/запись, постоянство, ...).

0

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

Самое важное, что нужно решить, это то, что является публичным vs/private, потому что именно так вы представляете свой класс для внешнего мира.

1

Защищенные данные имеют все недостатки публичных данных, поэтому вы можете сделать их общедоступными.

1

Если вы сделаете их конфиденциальными, а затем решите, что они должны быть доступны для производных классов, вы можете изменить их для защиты, не затрагивая какой-либо код, зависящий от этого класса. У вас также есть возможность добавления защищенных accessors.

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

Мое личное эмпирическое правило состоит в том, чтобы сделать все частным и продвигать их по цепочке видимости по мере необходимости.

1

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

Не вмешивайтесь в это, используйте запечатанное ключевое слово и сделайте его закрытым. Вы всегда можете реорганизовать позже, вам все равно придется.

+1

Стандарт C++ не имеет ключевого слова "запечатанное". –

+0

К сожалению, неверный тег. Ну, та же идея, но минус * запечатана *. –

+0

@downvoter - не обвиняйте меня в том, что язык отсутствует в ключевом слове. –

-1

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

0

Вы не делаете участников общедоступными по причине. Даже если эти члены могут быть свободно установлены или получены. Вы предоставляете фиктивные публичные сеттеры и геттеры. Для ТОЧНО ОДНОГО ПРИЧИНА, вы НЕ должны защищать членов. Вместо этого вы должны предоставить защищенные сеттеры и геттеры. Симметрия очень сильная.

+1

Я не согласен. См. Комментарий в сообщении sbi –

+1

@ Джон: Я даже понимаю, почему вы не согласны. Но я хочу сказать, что «ЕСЛИ» плохо, если бы публичные участники «ТОГДА» плохо защищать своих членов. Но, действительно, категорический запрет для публичных членов является спорным. –

+0

А, я понимаю, что вы сейчас говорите. –

1

Создание «public» создает контракт, который будет обязательным для всех унаследованных классов. Создание «защищенного» создает контракт с непосредственно - унаследованные классы, но не заставляет классы делать такой контракт доступным для любого из их потомков.

Если вы ожидаете, что исходный код базового класса будет доступен для всех, кто наследует его, и ожидает, что базовый класс никогда не изменится (как правило, это означает, что причиной использования базового класса является разрешение публичные члены и свойства базового класса, которые должны использоваться взаимозаменяемо с производными классами, а не уменьшать количество кода, необходимого для производных классов), и если все общедоступные методы являются виртуальными или напрямую связаны с виртуальными методами, нет большого недостатка в используя защищенные поля, а не защищенные виртуальные геттеры/сеттеры. Если класс потомка должен изменить способ использования полей, он может просто переопределить все используемые им методы.

Если вы ожидаете, что производные классы могут быть созданы в ситуациях, когда классы, находящиеся выше их в иерархии, не полностью известны или не являются неизменными, тогда может быть более уместным иметь защищенные геттеры/сеттеры, но эта ответственность может быть оставленный тем, кто создает «непрозрачные» слои в иерархии.

Пример: коллекция может содержать поле под названием «count». Базовая версия коллекции может хранить вещи таким образом, чтобы было удобно поддерживать поле, которое всегда содержит количество элементов, но производная версия может хранить вещи таким образом, чтобы это затруднило ситуацию. Имея поле под названием «count», базовый класс обещает своим прямым потомкам, что он будет поддерживать количество элементов в этом поле. Производный класс может хранить вещи по-другому, так что поле «count» не имеет смысла.Такой класс может затенять поле count с помощью свойства только для чтения; его потомки знали бы, что им нужно было прочитать свойство, а потомки первоначального класса знали бы, что они могут читать поле.

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

Приложение: единственное, что достигается путем добавления защищенных виртуальных геттеров и сеттеров, это возможность для производных классов изменять способ, которым код базового класса получает доступ к полям/свойствам. Иногда это необходимо, но чаще это создает проблемы. Например, если в коллекции часто могут быть удалены недавно добавленные элементы, производный класс может обернуть вещи так, чтобы последние несколько элементов были добавлены в небольшую «премиальную» коллекцию и переданы в основную коллекцию после добавления дополнительных дополнительных элементов. Код для основной коллекции будет ожидать свое собственное поле «count», чтобы указать, сколько элементов находится в основной коллекции. Если класс потомка переопределяет свойство count, чтобы включить его собственные элементы, основной код будет разорван. Класс потомков должен вместо этого указать поле count, чтобы его потомки увидели счет, который включает в себя бонусные элементы, но базовый класс по-прежнему будет видеть счет, который включает только его собственные элементы.

2

Лично я обычно стараюсь ограничить использование protectedvirtual вещами.

Я считаю, что любое другое использование protected концептуально эквивалентно public. В том смысле, что если вы пишете не виртуальный защищенный метод, напишите его так, как будто он был общедоступным. То же самое для поля. Если поле будет что-то сломать, если оно будет публичным, оно также сломает что-то, если оно будет защищено.

Это не значит, что плохо иметь защищенное поле или не виртуальный метод, это просто означает, что вы должны быть осторожны и преднамеренно с их использованием, и знаете, что кто-то может где-то использовать эти вещи и потенциально break ваш класс, просто выведя его.

+0

IMHO, защищенный виртуальный, иногда подходит, но затенение часто более уместно, чем переопределение. См. Добавление к моему ответу. – supercat

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