Как и в случае с объектами-членами, вам может понадобиться передавать аргументы конструктору базового класса. Это делается почти так же, как и изученная ранее передача аргументов конструктору объекта-члена ( смотрите приведённый ниже пример ).
GraduateStudent( char *pName , Advisor&adv , float qG=0.0 )
:Student( pName ) , advisor( adv ) , qualifierGrade(qG)
{
/* Код конструктора */
}
В этом примере конструктор класса GraduateStudent вызывает конструктор Student, передавая ему аргумент pName. Базовый класс конструируется до любых объектов-членов, а значит, конструктор класса Student вызывается перед конструктором Advisor. И только после конструктора члена Advisor начинает работу конструктор GraduateStudent.
В случае, когда в подклассе не указан явный вызов конструктора базового класса, вызывается конструктор по умолчанию базового класса. Таким образом, в следующем коде базовый класс Pig конструируется до членов LittlePig, несмотря на то, что конструктор LittlePig не делает явного вызова конструктора Pig.
class Pig
{
public :
Pig( ) : pHouse( 0 ) { }
protected :
House* pHouse ;
} ;
class LittlePig : public Pig
{
public :
LittlePig( float volStraw , int numSticks , int numBricks )
: straw( volStraw ) , sticks( numSticks ) , bricks( numBricks )
{ }
protected :
float straw ;
int sticks ;
int bricks ;
} ;
Аналогично, автоматически вызывается копирующий конструктор базового класса.
_________________
237 стр. Глава 20. Наследование классов
Деструкция подкласса...238
Следуя правилу о том, что деструкторы вызываются в порядке, обратном вызову конструкторов, первым вызывается деструктор GraduateStudent. После того как он выполнит свою работу, управление передаётся деструктору класса Advisor, а затем деструктору Student. Если бы Student был наследником класса Person, его деструктор получил бы управление после деструктора Student.
И это логично. Блок памяти сначала преобразуется в объект Student, а уже затем конструктор для GraduateStudent превращает этого студента в аспиранта. Деструктор же просто выполняет этот процесс в обратном направлении.
►Отношение СОДЕРЖИТ...238
Обратите внимание, что класс GraduateStudent включает в себя члены классов Student и Advisor, однако он включает их по-разному. Определяя данные-члены класса Advisor, вы знаете, что класс Student содержит внутри все данные-члены класса Advisor, но вы не можете сказать, что GraduateStudent ЯВЛЯЕТСЯ Advisor. Однако вы можете сказать, что GraduateStudent СОДЕРЖИТ Advisor. Какая разница между этим отношением и наследованием?
Используем в качестве примера автомобиль. Вы можете логически определить автомобиль как подкласс транспортных средств, а значит, он будет наследовать свойства остальных транспортных средств. С другой стороны, автомобиль содержит мотор. Если вы покупаете автомобиль, то покупаете и мотор ( если, конечно, вы не покупаете бывшую в употреблении машину там же, где я купил свою кучу металлолома ).
Если друзья пригласят вас приехать на воскресный пикник на новой машине и вы приедете на ней, никто не будет удивлён ( даже если вы явитесь на мотоцикле ), поскольку автомобиль ЯВЛЯЕТСЯ транспортным средством. Но если вы появитесь на своих двоих, неся в руках мотор, друзья решат, что вы попросту издеваетесь над ними, поскольку мотор не является транспортным средством, так как не имеет некоторых важных свойств, присущих транспортным средствам.
В аспекте программирования связь типа СОДЕРЖИТ достаточно очевидна. Разберём следующий пример:
class Vehicle
{
} ;
class Motor
{
} ;
class Car : public Vehicle
{
public :
Motor motor ;
} ;
void VehicleFn( Vehicle& v ) ;
void motorFn( Motor& m ) ;
_________________
238 стр. Часть 4. Наследование
int main( )
{
Car с ;
VehicleFn( с ) ; /* Так можно вызвать */
motorFn( c ) ; /* А так — нельзя */
motorFn( с.motor ) ; /* Нужно вот так */
return 0 ;
}