Абстрактные классы делят на интерфейсы и частично реализованные. Грань между ними такова:
• Интерфейс не имеет данных.
• У интерфейса все неабстрактные виртуальные методы представляют собой или эталонное поведение, или самую частую реализацию. В обоих случаях, если что, их надо не расширять, а переписывать с нуля.
Так вот, для интерфейсов таких конструкторов, разумеется, не нужно.
Например, между абстрактным потоком и файлом Win32 может быть такая иерархия: Stream → HandleStream → File. Stream — интерфейс, даже если там есть что-то типа
// virtual
unsigned long long Stream::remainder() const { return size() - pos(); }
HandleStream содержит уже данные (дескриптор Win32), и это уже частично реализованный класс, который крутится вокруг этого дескриптора: в деструкторе вызов CloseHandle, конструктор может принимать дескриптор, полученный каким-то «левым» образом.
HandleStream::HandleStream(HANDLE aHandle) : fHandle(aHandle) {}
HandleStream::~HandleStream() { close(); }
void HandleStream::close()
{
if (Handle != INVALID_HANDLE) { // не помню, как там эта константа в Win32
CloseHandle(fHandle);
fHandle = INVALID_HANDLE;
}
}
Вот в таких полуреализованных классах, разумеется, конструктор может инициализировать те данные, которые там есть.