2011-05-09 3 views
5

У меня есть простой, низкоуровневый контейнерный класс, который используется более высокоуровневым файловым классом. По сути, класс файла использует контейнер для хранения изменений локально, прежде чем сохранять окончательную версию в фактическом файле. Поэтому некоторые из методов переносятся непосредственно из класса контейнера в класс файлов. (Например, 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. 
    } 
} 
+0

Я вижу состав здесь, но не наследование. Я что-то упускаю? –

+0

@Doug Я спрашиваю, есть ли способ наследовать от класса контейнера, или 'MyContainer' в моем примере, всего за несколько методов. (Или лучшее решение, если оно есть.) Я отредактирую вопрос, чтобы быть более ясным. – Maxpm

+1

В этом случае кажется, что вы должны разделить MyContainer на класс, который имеет нужные вам методы (а затем наследовать) и остальные методы MyContainer в другом классе. – dlev

ответ

7

Ну, почему бы не просто наследовать приватно от 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. Если теперь сделать функцию с то же самое имя, она скрывает функцию базового и вы можете обернуть базовые функции, как это. Конечно, теперь нужно, чтобы в полной мере претендовать на вызов функции базового, или вы будете идти в бесконечную рекурсию.


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

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

Non-polymorphical если ваш MyClass не имеет виртуальный деструктор.

1

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

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

Я обычно пишу им нравится:

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

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

1

Рассмотрите, что имеет смысл в вашем случае - состав (есть) или наследование (есть) отношения между MyClass и MyContainer.

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

Если у вас есть сомнения, у вас есть, вероятно, хорошо.

EDIT: Я больше привык думать в Java/C# и не замечал, что C++ обладает большей гибкостью наследования, которую Xeo использует в своем ответе. В этом случае это просто приятное решение.

0

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

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

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

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

+0

Класс * делает * что-то делать. Мой пример был упрощен ради вопроса. – Maxpm