2016-07-15 2 views
2

Рассмотрим случай использования:Возможно ли заблокировать листинг ссылок?

  • у вас есть Copyable класс Base, для которых вы на самом деле не волнует, что происходит с ним.
  • Публикация публично из него является классом Derived, который не должен быть конвертирован в Base. Не за что. Даже ссылка на него, ссылка на Base, другими словами, должно быть незаконным для неявной привязки: Derived& -> Base&.

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

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

Вот простой пример, демонстрирующий проблему:

#include <iostream> 
using namespace std; 

class Derived; 

class Base 
{ 
public: 
    Base(int var) 
     : m_var(var) 
    { 
     std::cout << "Base default ctor with value: " << m_var << std::endl; 
    } 

    Base& operator=(const Derived&) = delete; 
    Base& operator=(Derived&&) = delete; 
    Base(const Derived&) = delete; 
    Base(Derived&&) = delete; 

    int m_var; 
}; 

class Derived : public Base 
{ 
public: 
    Derived(int var) 
     : Base(var) 
    { 
     std::cout << "Derived default ctor with value: " << m_var << std::endl; 
    } 

    Base unprotect() const 
    { 
     std::cout << "Derived unprotected with value: " << m_var << std::endl; 
     return Base(m_var); 
    } 
}; 

void foo(Base& base) 
{ 
    std::cout << "foo with value: " << base.m_var << std::endl; 
    // Base b2 = base; // just copied Derived, goal is to prohibit it! 
} 

int main() 
{ 
    Base b1(1); 
    foo(b1); 

    Derived d1(2); 
    foo(d1);   // is it at all possible to disallow implicit Derived& -> Base&? 
    // rationale is to require explicit: Base& Dervied::getBaseRef()  

    // Base b2 = d1; // illegal: error: use of deleted function 'Base::Base(const Derived&)' 

    return 0; 
} 
+2

_ «Наследование публично из него - это производный класс, который не должен быть конвертируемым в базу». _ Почему он наследует «Базу» тогда? Это звучит довольно странно. –

+1

Это скорее проблема дизайна, чем проблема с кодом. –

+0

@ πάνταῥεῖ из-за, к сожалению, большого кода * базы на основе 'Base' *, где явные приведения все равно будут использоваться, но вы могли бы отслеживать их * намного проще *. – hauron

ответ

3

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

Эти две вещи в значительной степени противоречат проектной точке ООП. Я не думаю, что на этом языке существует способ предотвратить обработку производного объекта в качестве публичной базы (путем неявного преобразования ссылки).

Вместо этого вы можете наследовать Base.

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

class Derived : Base 
{ 
public: 
    // ... 
    Base& base() { 
     return *this; 
    } 
}; 

Теперь вы можете заменить эти явные слепки с вызовом Derived::base, который остается разрешенным в то время как неявное преобразование не делает.

Derived d; 
Base& b = d;  // (implicit) conversion fails 
Base& b = d.base(); // this works 

Вы, вероятно, также хотят, чтобы реализовать версию функции const. Я оставлю это как упражнение.

1

Поскольку вы не наследуя заменить (явно сказать вы не хотите, чтобы когда-либо полученный посмотреть можно использовать в качестве основы), я Предположим, вы наследуете реализацию. В этом случае ответ будет ясным: Наследовать private ly или, возможно, protected в зависимости от ваших точных потребностей. Это точно запрещает все такие преобразования базовому классу, в то же время предоставляя вашему ребенку доступ к реализации базы.

Если есть один или два базовых элемента, вам нужно публично публиковать, вы всегда можете указать using их в производный класс.

3

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

class Derived : private Base { 
    public : 
    using Base::Base; 
    using Base::some_base_method; 
} 

int main(){ 
    Derived d1(2); 
    d1.some_base_method(); 
} 

Для плохого и худшего, это также делает много будущих дополнений к Базе неприступной через открытый интерфейс Derived. Он также хорошо работает с шаблонами, включая CRTP.

Смежные вопросы