Остановка или блокировка наследования в C ++ - PullRequest
4 голосов
/ 21 октября 2010

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

Вот пример:

#include <iostream>
#include <string>

struct Base
{
    virtual const std::string&  class_name(void) = 0;
};

struct Level1
    : public Base
{
private:  // Prevent child classes from overriding
          //     the Base::class_name method 
    const std::string& class_name(void)
        {
            static std::string name;
            name = "class" + class_name_from_level_1();
            return name;
        }
protected:
    // This is the "new" or redirected class that child classes
    //    must override.
    virtual const std::string& class_name_from_level_1(void) = 0;
};

struct Level2
    : public Level1
{
    static std::string  name;

    const std::string&  class_name_from_level_1(void)
        {
            if (name.length() == 0)
            {
                name = "Level2";
            }
            return name;
        }
};


int main(void)
{
    Level2  lev2;
    std::cout << lev2.class_name() << "\n";
    return 0;
}

Я получаю следующие ошибки от g++:

$ g++ hiding_virt_methods.cpp -o hiding_virt_methods.exe
hiding_virt_methods.cpp: In function `int main()':
hiding_virt_methods.cpp:15: error: `virtual const std::string& Level1::class_name()' is private
hiding_virt_methods.cpp:43: error: within this context

В приведенном выше примере я хочу следующую цепочку выполнения для Level2:
Base :: class_name () -> Level1 :: class_name_from_level_1 () -> Level2 :: class_name_from_level_1 ()

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

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

Редактировать: Пример из реального мира.
У меня есть интерфейс класса Record. Класс Record_With_Id наследуется от класса Record и добавляет поле идентификатора. Класс Record содержит метод accept_visitor. Класс Record_With_Id переопределяет accept_visitor для применения к полю идентификатора, затем вызывает виртуальный метод record_with_id_accept_visitor, который должны реализовать потомки.

Ответы [ 5 ]

2 голосов
/ 20 февраля 2012

C ++ 11 не добавил final и override?

http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final

2 голосов
/ 21 октября 2010

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

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

Стоит помнить, что публично производный класс является экземпляром базового класса, и СЛЕДУЕТ предоставлять интерфейс базового класса.

РЕДАКТИРОВАТЬ: вы можете редактировать "пример из реального мира", не могли бы вы объяснить проблему с обычной реализацией ...

#include <iostream>

struct Visitor
{
    virtual void operator()(int&) const = 0;
};

struct X
{
    virtual void visit(Visitor& v) { v(a); v(b); }
    int a;
    int b;
};

struct X_with_C : X
{
    int c;
    virtual void visit(Visitor& v) { X::visit(v); v(c); }
};

struct My_Visitor : Visitor
{
    void operator()(int& n) const { std::cout << ++n << '\n'; }
};

int main()
{
    X x;
    x.a = 10;
    x.b = 20;
    My_Visitor visitor;
    x.visit(visitor);
    X_with_C xc;
    xc.a = -10;
    xc.b = -20;
    xc.c = -30;
    xc.visit(visitor);
    X& rx = xc;
    rx.visit(visitor);
}

Выход:

11
21
-9
-19
-29
-8
-18
-28
1 голос
/ 12 апреля 2014

Четыре года спустя, позвольте мне добавить, что C ++ 11 ввел ключевое слово final:

class Base final {

Это также может быть применено к виртуальным методам:

class Base{
 protected: 
        virtual void doWork() = 0;
 public:
        virtual void startWork() final { doWork(); }

};


class Derived: public Base{
 protected:
        virtual void doWork() override { /* some work */ }
 public:
       //  error: overriding final function ‘virtual void Base::startWork()’
        virtual void startWork() override { /* something else */ } 


};
0 голосов
/ 21 октября 2010

Похоже, вы пытаетесь сделать что-то сложным образом.

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

#include <iostream>
#include <string>

struct Base
{
    virtual std::string class_name() const = 0;
};

class Level1
    : public Base
{
public:
    std::string class_description() const
    {
        return "class " + class_name();
    }
};

class Level2
    : public Level1
{
public:
    virtual std::string class_name() const
    {
       return "Level2";
    }
};


int main()
{
    Level2  lev2;
    std::cout << lev2.class_description() << "\n";
}

В приведенном выше коде я предположил, что это для отладки / трассировки или чего-то в этом роде. В целях идентификации загляните в typeid (встроенный оператор).

Приветствия и hth.,

0 голосов
/ 21 октября 2010

В Visual Studio 2005 и выше реализовано ключевое слово «sealed», которое является расширением Microsoft для C ++. Вы помещаете это в объявление Level1 :: class_name (). Я не думаю, что есть портативный способ.

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