Когда виртуальный метод должен быть чистым? - PullRequest
9 голосов
/ 27 июля 2011

Я нашел какой-то код, над которым я работаю, и мне было интересно, какова лучшая реализация дизайна.

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

virtual void AMethod1() {}                 // 1
virtual void AMethod2() {assert(false);}   // 2
virtual void AMethod3() = 0;               // 3
  1. Текущий код.
  2. Идея 1: предупреждает пользователя, что этот производный объект не реализовал тело этого метода.
  3. Идея 2: Заставляет производные классы реализовывать тело, пустое или нет.

Что вы, доверяющие удивительные ТАК люди, думаете?


Edit1: После публикации (и чтения ответов) я понимаю, что утверждать это плохо!

virtual void AMethod3() = {throw (ENotImplemented)};               // 4

Ответы [ 10 ]

9 голосов
/ 27 июля 2011

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

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

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

Как обычно, C ++ поддерживает несколько парадигм, и вы можете выбрать ту, которая вам больше нравится.

6 голосов
/ 27 июля 2011

Вам следует использовать вариант 3, если производный класс должен реализовать этот метод. Используйте опцию 1, если реализация в производном классе является необязательной. Избегайте варианта 2 вообще.

3 голосов
/ 27 июля 2011

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

Это зависит от того, хотите ли вы заставить производные классы переопределить метод.Если вы это сделаете, то используйте чистую virtual;это языковая поддержка именно для этого требования.Если есть или может быть на более позднем этапе реализация по умолчанию amethod, тогда используйте чистый метод virtual с реализацией:

class Foo {
    virtual void amethod() = 0;
};

void Foo::amethod() {
    // whatever
}

Функция теперь все еще чиста virtual, поэтомуКласс Foo не может быть создан, но любой производный класс наследует реализацию, и его методы могут вызывать его как Foo::amethod.

2 голосов
/ 27 июля 2011
  • virtual void AMethod1() = 0;: чистый виртуальный компьютер лучше всего подходит, если в вашем базовом классе нет реализации, обеспечивающей И при реализации этого поведения СЛЕДУЕТ *, (Это вариант 3 в вашем вопросе)

  • virtual void AMethod1() {}: виртуальная среда с пустой реализацией является наилучшей, когда в вашем базовом классе нет реализации, обеспечивающей И при таком поведении МОЖЕТ быть реализованным. (Это вариант 1 в вашем вопросе)

  • virtual void AMethod1() { assert(false); }: По моему мнению, следует избегать виртуального с assert(false). Я не вижу никакого действительного использования для этого. Обоснование этого заключается в том, что все варианты использования охватываются двумя вышеупомянутыми вариантами: либо поведение может , либо должно реализовано, поэтому нет смысла определять вызываемый метод, который всегда выходит из строя. Компилятор может обработать это для вас, предотвратив этот вызов, поэтому эта опция создает риск, откладывая эту проверку во время выполнения. (Это вариант 2 в вашем вопросе)

2 голосов
/ 27 июля 2011

Я видел довольно много примеров, подобных этому, где вам нужно создать экземпляр класса, поэтому вы используете virtual с пустым телом:

virtual void AMethod1() {}                 // 1

Используйте это, когда хотите принудительно вызвать производный классчтобы переопределить эту функцию, и вам не нужно значение по умолчанию:

virtual void AMethod3() = 0;               // 3

Так что это действительно зависит от того, что вы хотите сделать.

2 голосов
/ 27 июля 2011

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

1 голос
/ 27 июля 2011

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

Это зависит от вашего дизайна. Если ваш метод чисто виртуальный, вы отправляете разработчику производного класса сообщение, в котором говорится, что «вы должны разместить здесь какой-то фактический код, чтобы ваш класс работал». С другой стороны, если ваш метод виртуальный с пустым телом, сообщение будет таким: «Вы можете разместить здесь некоторый код, но он соответствует вашим реальным потребностям».

virtual void AMethod1() {}                 // 1
virtual void AMethod2() {assert(false);}   // 2
virtual void AMethod3() = 0;               // 3

Я бы определенно предпочел вариант 3, а не 2, он даст ошибку компиляции вместо ошибки времени выполнения, если производный класс не реализует виртуальный метод.

1 голос
/ 27 июля 2011

Так как вам нужен механизм virtual;Вот мой короткий ответ:

(1) virtual void AMethod1() {}

Требование:

- Allow creating objects of base class
- Base `virtual` method is use.

(2) virtual void AMethod2() {assert(false);}

Требование:

- Allow creating objects of base class
- Base method is not used
- Force derived classes to implement the method (hard way, because it happens at runtime).

(3) virtual void AMethod3() = 0;

Требование:

- Don't allow base class object creation
- Force derived classes to implement the method at compile time
1 голос
/ 27 июля 2011

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

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

Простой, например,

class alpha     {
     public:virtual void show()=0; //pure virtual function
    };

class beta : public alpha {

     public:void show()   //overriding
        {
         cout<<"OOP in C++";
        }
    };
void main() {
     alpha *p;
     beta b;
     p=&b;
     p->show();
   }
0 голосов
/ 27 июля 2011

Простого правила не существует:

Использовать 1 (пустая реализация), если для некоторых производных классов имеет смысл ничего не делать, если вызывается функция.

Использовать 3, если нетДля производного класса не имеет смысла не реализовывать функцию.

Используйте 2 в редком случае, когда предварительным условием функции является то, что другая виртуальная функция вернула true, и эта функция имеет реализацию по умолчанию, которая возвращает false(Или что-то вдоль этих линий).В основном, если часть интерфейса является необязательной.(Но обычно в таких случаях лучше получить интерфейс; классы, реализующие расширенный интерфейс, получают его, и клиенты, желающие использовать его dynamic_cast для расширенного интерфейса.)

Из опыта (но ваше программированиестиль может быть другим), 1, кажется, применяется по крайней мере 90% времени, и я думаю, что за двадцать лет C ++ я использовал 3 один раз, или, может быть, дважды.

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