Копирование методов от члена - PullRequest
5 голосов
/ 10 мая 2011

У меня есть простой контейнерный класс низкого уровня, который используется файловым классом более высокого уровня. По сути, класс файла использует контейнер для локального хранения изменений перед сохранением окончательной версии в реальном файле. Поэтому некоторые методы переносятся непосредственно из класса контейнера в класс файла. (Например, Resize().)

Я только что определил методы в классе файла для вызова их вариантов класса контейнера. Например:

void FileClass::Foo()
{
    ContainerMember.Foo();
}

Это, однако, становится неприятностью. Есть ли лучший способ сделать это?

Вот упрощенный пример:

class MyContainer
{
    // ...

    public:

    void Foo()
    {
        // This function directly handles the object's
        // member variables.
    }
}

class MyClass
{
    MyContainer Member;

    public:

    void Foo()
    {
        Member.Foo();

        // This seems to be pointless re-implementation, and it's
        // inconvenient to keep MyContainer's methods and MyClass's
        // wrappers for those methods synchronized.
    }
}

Ответы [ 5 ]

7 голосов
/ 10 мая 2011

Ну, почему бы просто не унаследовать приватно от MyContainer и предоставить те функции, которые вы хотите просто переслать с объявлением using?Это называется "Реализация MyClass в терминах MyContainer.

class MyContainer
{
public:
    void Foo()
    {
        // This function directly handles the object's
        // member variables.
    }

    void Bar(){
      // ...
    }
}

class MyClass : private MyContainer
{
public:
    using MyContainer::Foo;

    // would hide MyContainer::Bar
    void Bar(){
      // ...
      MyContainer::Bar();
      // ...
    }
}

Теперь" извне "сможет напрямую вызывать Foo, тогда как Barдоступно только внутри MyClass. Если вы теперь создаете функцию с тем же именем, она скрывает базовую функцию и вы можете обернуть базовые функции таким образом. Конечно, теперь вам нужно полностью квалифицировать вызов базовой функции, иливы попадете в бесконечную рекурсию.


Кроме того, если вы хотите разрешить (неполиморфное) создание подклассов MyClass, чем это одно из редких мест, где защищенное наследование фактическиполезно:

class MyClass : protected MyContainer{
  // all stays the same, subclasses are also allowed to call the MyContainer functions
};

Неполиморфный, если у вашего MyClass нет виртуального деструктора.

1 голос
/ 10 мая 2011

Подумайте, что имеет смысл в вашем случае - композиция (имеет) или наследование (есть) отношения между MyClass и MyContainer.

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

Если сомневаетесь, то, что у вас, вероятно, в порядке.

РЕДАКТИРОВАТЬ: я более привык думать в Java / C # и упустил из виду тот факт, что C ++ обладает большей гибкостью наследования, которую Xeo использует в своем ответе.Это просто хорошее решение в этом случае.

1 голос
/ 10 мая 2011

Это наследование делегирования , и я не знаю, что C ++ предлагает какой-либо механизм, чтобы помочь с этим.

1 голос
/ 10 мая 2011

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

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

Я обычно пишу ихкак:

void Foo()      { return Member.Foo(); }
int  Bar(int x) { return Member.Bar(x); }

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

0 голосов
/ 10 мая 2011

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

Но реальная проблема в этом вопросе состоит в том, что у класса нет поведения.Это просто обертка, которая ничего не делает.Каждый класс должен делать что-то другое, чем просто передавать данные.

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

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

...