Проще «Предотвращение производных классов» в C ++ - PullRequest
8 голосов
/ 04 апреля 2011

Исходя из предположения, что существует законная причина для предотвращения деривации из какого-то класса, Бьярне дает решение здесь для ответа на вопрос «Могу ли я остановить людей, производящих от моего класса?»

Однако я подумал:

class final {
protected:
  final() { }         // line 3
};

class B : private virtual final {
};

class D : public B {  // line 9
};

int main() {
  B b;
  D d;                // line 14
}

При попытке компиляции вы получите:

foo.cpp: In constructor ‘D::D()’:
foo.cpp:3: error: ‘final::final()’ is protected
foo.cpp:9: error: within this context
foo.cpp: In function ‘int main()’:
foo.cpp:14: note: synthesized method ‘D::D()’ first required here 

Работает ли мое решение для всех случаев?ИМХО, это лучше, потому что класс final является универсальным и не требует предварительного знания класса, чтобы предотвратить вывод из.

Ответы [ 2 ]

6 голосов
/ 04 апреля 2011

Решение довольно плохое, его можно улучшить с помощью CRTP (это делает Adobe), но оно не будет полным решением. Проблема с вашим кодом заключается в том, что другой программист, который не хочет нарушать ваш контракт (она хороший человек), но не знает, что она не должна быть производной от вашего класса, может захотеть заблокировать других от получения из своего собственного класса:

class YourSealedClass : private virtual final {};
class HerSealedClass : public YourSealedClass, private virtual final {
};

Обратите внимание, что нет даже злого умысла, и договор расторгнут. Улучшение с CRTP будет:

template <typename T>
class seal
{
protected:
   seal(){}
   ~seal(){}
};
class YourSealedClass : private virtual seal<YourSealedClass> {};

Это поймает предыдущую ошибку, так как ее код будет:

class HerSealedClass : public YourSealedClass, private virtual seal<HerSealedClass> {};

И поскольку она не наследует от seal<YourSealedClass>, компилятор ее догонит.

Проблема этого подхода заключается в том, что он не блокирует некоторых упрямых (или злых) программистов от написания:

class MalignClass : public YourSealedClass, private virtual seal<YourSealedClass> {};

Стать ребенком в классе запечатывания и получить доступ к своему классу.

В C ++ 0x я верю (придется перепроверить его), что они снимают ограничение на то, что шаблон не может подружиться с аргументом шаблона, и это будет хорошим шагом в универсальном типобезопасном решении для герметика класса:

template <typename T>
class seal {
   friend class T; // illegal in C++03
   seal() {};
   ~seal() {};
};
class MySealedClass : private virtual seal<MySealedClass>
{};

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

2 голосов
/ 04 апреля 2011

Попробуйте:

class D : public B, public virtual final
{
};
...