Предотвращение реализации виртуального метода в C ++ - PullRequest
4 голосов
/ 15 февраля 2009

У меня есть следующая иерархия классов в C ++:

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }

    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

Я хотел бы гарантировать, что классы на уровне реализации не предоставляют свою собственную реализацию apply, и что они реализуют только производную_специальность. Есть ли способ гарантировать, что классы, наследуемые от Derived, не будут реализованы apply, чтобы использовалась реализация Derived :: apply? Насколько я понимаю, в C ++ метод, сделанный виртуальным в базовом классе, является виртуальным на всем протяжении иерархии наследования, но если в C ++ есть какие-то хитрости, мне было бы интересно узнать о них.

Я всегда удивляюсь тому, что разрешено в C ++, поэтому я подумал, что стоит спросить. :)

Ответы [ 8 ]

21 голосов
/ 15 февраля 2009

Вы можете сделать реализацию классом делегата, а не специализацией Derived

class Derived : public Base
{
    Derived()

    void apply() 
    {
        //whatever, delegate to impl class instance
        impl->apply_specialization();
    }


    Impl* impl;
};

class Impl : public WhateverImplInterface
{
      void apply_specialization(){}
};

Тогда реализация не имеет доступа к функции apply и отделена от иерархии. Затем производный класс параметризуется экземпляром класса Impl.

5 голосов
/ 15 февраля 2009

«Я бы хотел гарантировать, что классы на уровне реализации не предоставляют свою собственную реализацию apply».

Вы не можете.

Ни один из примеров, которые я видел до сих пор, не мешал ни одному из производных классов определить свою собственную функцию применения. Все они предоставляют методы для моделирования отношений между apply и output_specialty, предлагая пользователям, что они не должны применять override Вы можете достичь того же в линии документации, однако.

Что вам нужно, так это окончательное утверждение Java, которого нет в C ++, верно?

5 голосов
/ 15 февраля 2009

Укажите ограничение в вашей документации.

5 голосов
/ 15 февраля 2009

Вы можете сделать это по составу:

class Base {
    virtual void apply();
};

class Derived : public Base {

    class IImplementation {
        virtual void derived_specialty() = 0;
    };

    IImplementation& m_implementation;

    Derived(IImplementation& implementation)
        : m_implementation(implementation)
    {}

    virtual void apply() {
        // implementation here that uses m_implementation.derived_specialty
    }

};


class Implementation : Derived::IImplementation {   
    virtual void derived_specialty() {
        // implementation
    }
};

Другие классы все еще могут быть подклассом Derived и переопределять метод apply, но ваш класс реализации больше не является одним из этих классов.

4 голосов
/ 15 февраля 2009

Вы можете поместить утверждение в деструктор, чтобы убедиться, что apply не было переопределено:

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }
    virtual ~Derived() {
        assert(this->apply == Derived::apply);
    }
    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

Идея в том, что this-> apply получит адрес метода из виртуальной таблицы, а Derived :: apply разрешит его во время компиляции. Если они равны, применение не было переопределено снова в классе реализации. Преимущество этого подхода состоит в том, что он не налагает снижения производительности в сборке выпуска, где макросы assert () (должны быть) удалены из сгенерированного кода.

4 голосов
/ 15 февраля 2009

Вы можете сделать Base :: apply не виртуальным, а также использовать шаблонный шаблон в Base.

Эта статья объясняет преимущества этой практики:
http://www.gotw.ca/publications/mill18.htm

1 голос
/ 05 мая 2009

Всегда есть модификаторы доступа:

 class base {
      protected: virtual void real_apply() = 0;
 };
 class derived : public base {
      void real_apply();
 public:
      apply() { real_apply(); }
 };
 class other : public derived {
      void func() {
          apply();      // this is ok
          real_apply(); // this is a compile time error
      }
 };
1 голос
/ 15 февраля 2009

Попробуйте использовать шаблон метода pattern

Википедия имеет пример C ++.

Это не меняет инкапсуляцию, но улучшает дизайн, поэтому вам не нужно.

...