Как и в случае с объектами-членами, вам может понадобиться передавать аргументы конструктору базового класса. Это делается почти так же, как и изученная ранее передача аргументов конструктору объекта-члена ( смотрите приведённый ниже пример ).
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 ;
}