Запретить подкласс вне заголовка

У меня есть три класса, определенные в файле .h:

class Base
{ // number of pure virtual functions };

class Derived1 : public Base
{ // number of pure virtual functions };

class Derived2 : public Base
{ // number of pure virtual functions };

Я хочу, чтобы пользователи этого заголовочного файла были только подклассифицированы Derived1 или Derived2. Я хочу запретить пользователям подклассы Base. Я "мог" использовать ключевое слово "final" для класса Base, но это помешало бы мне от подкласса в моем заголовочном файле. Мне нужно, чтобы все вышеупомянутые классы в файле заголовка были необходимы пользователю для определения определений методов в классах Base и DerivedX.

Я думаю о чем-то вроде ограничения области наследования в файл заголовка (аналогично статической переменной). Любые предложения или идеи будут высоко оценены.

Ответ 1

Вы можете сделать конструктор Base закрытым и иметь эти два класса друзей, например.

#include <iostream>

class Base
{
private:
    Base() {}
    friend class Derived1;
    friend class Derived2;
};

class Derived1 : public Base
{
};

class Derived2 : public Base
{
};

int main() {

    Derived1 obj;
    Base obj2; // Error

    return 0;
}

http://ideone.com/NUWad0

Также обратите внимание: copy/move конструктор базового класса должен также быть закрытым. Я не добавляю код, чтобы сосредоточиться на основном предложении, но вы тоже должны подумать об этом.


Изменить в отношении конструкторов друзей.

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

class Derived1;
class Derived2;

class Base
{
private:
    Base() {}
    friend Derived1::Derived1(); // Error - incomplete type 'Derived1' in nested name specifier
};

class Derived1 : public Base
{
    public:
    Derived1() {};
};

...    

и вы не можете перенаправить объявление базового класса (вы не знаете, сколько места выделяется для производного объекта):

class Base;

class Derived1 : public Base // Error: base class has incomplete type
{
    public:
    Derived1() {};
};

class Base
{
private:
    Base() {}
    friend Derived1::Derived1();
};

...

поэтому в вашем конкретном случае вам лучше с доступом к классу друзей, если вы не хотите что-то изменить в своем дизайне.