среда, 7 июля 2010 г.

Final class в С++

Последнее время приходится проводить много собеседований. С одной стороны занимает много времени и сил, а с другой дополнительный стимул расти самому.
Вот недавно всплыл такой вопрос: можно ли запретить наследование от класса в С++. В Java есть такое понятие как final класс, т.е. класс от которого нельзя наследоваться.

Предположим есть некий класс А:

class A
{
public:
A(){}
~A(){}
};

и класс B

class B : public A //все отлично собирается
{
};


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

Первое, что приходит в голову это сделать конструктор и деструктор класса А приватными. Действительно после этого я не смогу наследоваться, но возникает другая проблема - я не могу создавать экземпляры класса А.
Пытаемся развить мысль далее. Что если переопределить для класса А операторы new и delete. Так как это будут операторы класса, то они смогут получить доступ к приватным конструктору и деструктору. Получается вот такой класс:

class A
{
private:
A(){}
~A(){}


public:
void* operator new(size_t size)
{
return ::operator new(size);
}

void operator delete(void* block)
{
::operator delete(block);
}
};

сlass B : public A //компилятор ругается
{
};


Но на самом деле это не работает. Переопределенные операторы new\delete все равно не получают доступа к приватным конструктору и деструктору класса А. Объявление операторов new\delete друзьями класса А тоже не помогает.

Второй мыслью (после часа попыток) было полезть во всемирную паутину и найти ответ там. Ответ я нашел, но не на все вопросы. Итак реализация final класса для С++ выглядит следующим образом:

class ClassLocker
{
friend class A;
private:
ClassLocker(){}
};

class A : public virtual ClassLocker
{
public:
A(){}
~A(){}
};

class B : public A //компилятор выдает сообщение об ошибке
{
};

Самое интересное, что если мы убрать виртуальное наследование класса А от класса ClassLocker, т.е. написать class A : public ClassLocker, то все отлично собирается. Слово virtual в данном случае является ключевым.

Итак в итоге есть два вопроса:
  • Почему переопределенные операторы new\delete не имеют доступа к приватном конструктору и деструктору?
  • Почему именно виртуальное наследование запрещает дальнейшее наследование?
Ответы на вопросы я дам в следующей статье.

Всем доброй ночи.