2016-04-05 5 views
3

В настоящее время я реорганизую некоторый унаследованный код и хотел бы разложить множитель, если ... elseif ... в серию классов, реализующих различные стратегии.Вывести из класса, объявленного в закрытом виде

Поскольку я должен получить доступ к внутренним объектам исходного объекта, я собираюсь объявить новые классы как вложенные классы; так как никто из внешнего мира не должен обращаться к ним, я бы предпочел объявить их в частном порядке.

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

- заголовок файла

class MyUglyClass 
{ 
private: 
    class IStrategyBase; 
    IStrategyBase* sPtr; 
    // class ActualImplementation; // this is what I'd like to avoid 
    // class YetAnotherImplementation; // as above 
    // blah blah blah 
}; 

- файл реализации

class MyUglyClass::IStrategyBase 
{ 
    virtual ResultType DoSomething(SomeType someParameter) = 0; 
    // could expose some MyUglyClass members, since 
    // derived classes wouldn't inherit friendship 
}; 

class ActualImplementation: public MyUglyClass::IStrategyBase 
{ 
    ResultType DoSomething(SomeType someParameter) override 
    { 
     // Do actual work 
    } 
} 

class YetAnotherImplementation: public MyUglyClass::IStrategyBase 
{ 
    ResultType DoSomething(SomeType someParameter) override 
    { 
     // Doing something really tricky & clever for corner cases 
    } 
} 

Конечно, компилятор жалуется, поскольку IStrategyBase не доступен; Я мог бы обойти это с помощью fwd-объявления ActualImplementation и YetAnotherImplementation в файл заголовка вместе с IStrategyBase, но я бы предпочел избежать этого, так как мне понадобится изменить заголовок, если потребуется новая стратегия.

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

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

Есть ли способ достичь этого, я не могу пропустить?

EDIT:

Спасибо всем людям, которые комментировали, я понял, что никто не мог связываться с IStrategyBase класса, даже если они будут объявлены в общественной сферы, так как определение класса будет похоронена в файле реализации, а также. Теперь мне интересно, могу ли я сделать производные классы доступными внутренними компонентами MyUglyClass без необходимости объявлять их вместе с IStrategyBase. Я думаю, что ответ «нет», поскольку дружба не унаследована, но, возможно, есть еще кое-что на C++, которого я пропускаю.

+3

Даже если вы объявляете публичную публикацию 'IStrategyBase', никто не может ее испортить. – molbdnilo

+0

В моем понимании, другие люди могли бы извлечь из этого.Если 'IStrategyBase' раскрывал некоторые детали реализации' MyUglyClass', они были бы доступны любому. –

+3

Вы не можете получить из класса, который только объявлен и не определен. –

ответ

3

Одним из возможных вариантов (это не Pimpl идиомы, просто доступность хак):

class MyUglyClass 
{ 
private: 
    struct Impl; // Is automatically "friend struct Impl;" 
    class IStrategyBase; 
    IStrategyBase* sPtr; 
    // class ActualImplementation; // this is what I'd like to avoid 
    // class YetAnotherImplementation; // as above 
    // blah blah blah 
}; 

class MyUglyClass::IStrategyBase 
{ 
public: 
    virtual int DoSomething(int someParameter) = 0; 
    // could expose some MyUglyClass members, since 
    // derived classes wouldn't inherit friendship 
}; 

struct MyUglyClass::Impl 
{ 
    class ActualImplementation: public MyUglyClass::IStrategyBase 
    { 
     int DoSomething(int someParameter) override 
     { (void) someParameter; return 1;} 
    }; 

    class YetAnotherImplementation: public MyUglyClass::IStrategyBase 
    { 
     int DoSomething(int someParameter) override 
     { (void) someParameter; return 2; } 
    }; 
}; 

int main() {} 
2

Если вы хотите, чтобы скрыть какие-либо детали imlementation вы можете использовать Pimpl идиомы (указатель на реализацию) ака непрозрачный указатель https://en.wikipedia.org/wiki/Opaque_pointer Таким образом, вы можете изменить свой код, как этот

- заголовочный файл

#include <memory> 

class MyUglyClass 
{ 
    MyUglyClass(); 
    ~MyUglyClass(); // destructor must be only declared to avoid problems 
        // with deleting just forwarded inner class 
private: 
    class Impl; 
    std::unique_ptr<Impl> pImpl; 
}; 

- - файл реализации

class MyUglyClass::Impl 
{ 
    class IStrategyBase; 
    IStrategyBase* sPtr; 
    class ActualImplementation; // now these classes safely hidden inside .cpp 
    class YetAnotherImplementation; // Nobody can reach them. 

}; 

class MyUglyClass::Impl::IStrategyBase 
{ 
    virtual ResultType DoSomething(SomeType someParameter) = 0; 
    // could expose some MyUglyClass members, since 
    // derived classes wouldn't inherit friendship 
}; 

class ActualImplementation: public MyUglyClass::Impl::IStrategyBase 
{ 
    ResultType DoSomething(SomeType someParameter) override 
    { 
     // Do actual work 
    } 
}; 

class YetAnotherImplementation: public MyUglyClass::Impl::IStrategyBase 
{ 
    ResultType DoSomething(SomeType someParameter) override 
    { 
     // Doing something really tricky & clever for corner cases 
    } 
}; 

MyUglyClass::MyUglyClass() : pImpl(new Impl()) {} 

MyUglyClass::~MyUglyClass() {} // let the unique_ptr do its work 
Смежные вопросы