Предотвратите наследование классов в C++

Недавно один из моего друга спросил меня, как предотвратить наследование классов в C++. Он хотел, чтобы компиляция перестала работать.

Я думал об этом и нашел 3 ответа. Не уверенный, который является лучшим.

1) Частный конструктор (конструкторы)

class CBase
{

public:

 static CBase* CreateInstance() 
 { 
  CBase* b1 = new CBase();
  return b1;
 }

private:

 CBase() { }
 CBase(CBase3) { }
 CBase& operator=(CBase&) { }


};

2) Используя базовый класс CSealed, частный ctor и виртуальное наследование

class CSealed
{

private:

 CSealed() {
 }

 friend class CBase;
};


class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

3) Используя базовый класс CSealed, защищенный ctor и виртуальное наследование

class CSealed
{

protected:

 CSealed() {
 }

};

class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

Все вышеупомянутые методы удостоверяются, что класс CBase не может быть наследован далее. Мой Вопрос:

1) Который является лучшим методом? Какие-либо другие доступные методы?

2) Метод 2 и 3 не будет работать, если класс CSealed не будет наследован virutally. Почему это? Это имеет какое-либо отношение к vdisp ptr??

PS:

Вышеупомянутая программа была скомпилирована в компиляторе C++ MS (Visual Studio). ссылка: http://www.codeguru.com/forum/archive/index.php/t-321146.html

56
задан bmargulies 2 February 2010 в 13:05
поделиться

6 ответов

Вы не можете предотвратить наследование (до ключевого слова C++11 final) - вы можете предотвратить только инстанцирование наследуемых классов. Другими словами, нет никакого способа предотвратить:

class A { ... };

class B : public A { ... };

Лучшее, что Вы можете сделать, это предотвратить инстанцирование объектов типа B. Исходя из этого, я предлагаю воспользоваться советом kts и задокументировать тот факт, что A (или что-то в этом роде) не предназначен для наследования, дать ему невиртуальный деструктор, и никакие другие виртуальные функции, и оставить все как есть.

11
ответ дан 26 November 2019 в 17:22
поделиться

Вы проходите через конторшны, чтобы предотвратить дальнейшее подклассирование. Почему? Зафиксируйте тот факт, что класс не является расширяемым, и сделайте dtor невиртуальным. В духе с, если кто-то действительно хочет игнорировать то, как вы намеревались использовать это, зачем их останавливать? (Я также никогда не видел смысла окончательных классов/методов в java).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};
-121--989134-

При простой настройке (когда сервер приложений сервлетов принимает запрос клиента напрямую) оба метода возвращают одно и то же значение.

Если контейнер сервлета не является приложением, принимающим запрос клиента (например, при использовании HTTP-сервера Apache с mod_jk для принятия запроса и пересылки запроса экземпляру Tomcat через AJP), то getServerPort () вернет порт, к которому подключен клиент (вероятно, 80, когда используется порт по умолчанию) и getLocalPort () вернет порт, который Tomcat использовал для приема соединения с HTTP-сервера (вероятно, 8009 или что-то подобное).

-121--2203114-

1) - это вопрос вкуса. Если я вижу это правильно, ваши более причудливые 2-е и 3-е решения перемещают ошибку в определенных обстоятельствах со времени ссылки на время компиляции, которое в целом должно быть лучше.

2) Виртуальное наследование необходимо для принудительной инициализации (виртуального) базового класса наиболее производным классом, из которого ctor базового класса уже недоступен.

5
ответ дан 26 November 2019 в 17:22
поделиться

Вы проходите через искажения, чтобы предотвратить дальнейшие подклассификации. Почему? Задокументируйте тот факт, что класс не является расширяемым и сделайте дтор невиртуальным. В духе c, если кто-то действительно хочет проигнорировать то, как вы собирались это использовать, зачем останавливать его? (Я никогда не видел смысла заключительных классов/методов и в java).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};
10
ответ дан 26 November 2019 в 17:22
поделиться

Если можно, я бы выбрал первый вариант (частный конструктор). Причина в том, что практически любой опытный программист на Си++ увидит это с первого взгляда и сможет распознать, что вы пытаетесь предотвратить подклассификацию.

Могут быть и другие, более хитрые методы предотвращения подклассов, но в этом случае чем проще, тем лучше.

1
ответ дан 26 November 2019 в 17:22
поделиться

To ответьте на ваш вопрос, вы не можете наследовать от CBase, потому что при виртуальном наследовании производный класс должен иметь прямой доступ к классу, от которого он был унаследован виртуально. В этом случае класс, производный от CBase, должен иметь прямой доступ к CSealed, чего он не может, поскольку конструктор является частным.

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

template<class T>
class CSealed
{
    friend T;    // Don't do friend class T because it won't compile
    CSealed() {}
};

class CBase : private virtual CSealed<CBase>
{
};
4
ответ дан 26 November 2019 в 17:22
поделиться

Еще одно решение:

template < class T >
class SealedBase
{
protected:
    SealedBase()
    {
    }
};

#define Sealed(_CLASS_NAME_) private virtual SealedBase<_CLASS_NAME_>


#include "Sealed.h"

class SomeClass : Sealed(Penguin)
{
};
-1
ответ дан 26 November 2019 в 17:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: