Стефан Дэвис - С++ для чайников .
- Название:С++ для чайников .
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс. Компьютерное издательство Диалектика
- Год:2007
- Город:Москва
- ISBN:0-7645-6852-3, 978-5-8459-0723-3
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стефан Дэвис - С++ для чайников . краткое содержание
1
empty-line
4
С++ для чайников . - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
►Отношение СОДЕРЖИТ...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 ;
}
Вызов VehicleFn( с ) допустим, поскольку с ЯВЛЯЕТСЯ Vehicle . Вызов motorFn( с ) недопустим, поскольку с — не Motor , хотя он и содержит Motor . Если возникает необходимость передать функции только ту часть с , которая является мотором, это следует выразить явно: motorFn( с.motor ) .
_________________
239 стр. Глава 20. Наследование классов
ОГЛАВЛЕНИЕ
В этой главе...
►Зачем нужен полиморфизм 243
►Как работает полиморфизм 245
►Когда функция не является виртуальной 246
►Виртуальные особенности 247
Количество и тип аргументов функции включены в её полное или, другими словами, расширенное имя. Это позволяет создавать в одной программе функции с одним и тем же именем ( если различаются их полные имена ):
void someFn( int )
void someFn( char* )
void someFn( char* , double )
Во всех трёх случаях функции имеют одинаковое короткое имя someFn( ) . Полные имена всех трёх функций различаются: someFn( int ) отличается от someFn( char* ) и т.д. С++ решает, какую именно функцию нужно вызвать, рассматривая полные имена слева направо.
«Тип возвращаемого значения не является частью полного имени функции, поэтому вы не можете иметь две функции с одинаковым расширенным именем, отличающиеся только типом возвращаемого объекта.»
[ Атас! ]
Итак, функции-члены могут быть перегружены. При этом помимо количества и типов аргументов расширенное имя функции-члена содержит ещё и имя класса.
С появлением наследования возникает небольшая неувязка. Что, если функция-член базового класса имеет то же имя, что и функция-член подкласса? Попробуем разобраться с простым фрагментом кода:
class Student
{
public :
float calcTuition( ) ;
} ;
class GraduateStudent : public Student
{
public :
float calcTuition( ) ;
} ;
int main( int argcs , char* pArgs[ ] )
{
Student s ;
GraduateStudent gs ;
s.calcTuition( ) ; /* Вызывает Student::calcTuition( ) */
gs.calcTuition( ) ; /* Вызывает GraduateStudent::calcTuition( ) */
return 0 ;
}
_________________
240 стр. Часть 4. Наследование
Как и в любой ситуации с перегрузкой, когда программист обращается к calcTuition( ) , С++ должен решить, какая именно функция calcTuition( ) вызывается. Если две функции отличаются типами аргументов, то нет никаких проблем. Даже если аргументы одинаковы, различий в именах класса достаточно, чтобы решить, какой именно вызов нужно осуществить, а значит, в этом примере нет ничего необычного. Вызов s.calcTuition( ) обращается к Student::calcTuition( ) , поскольку s локально объявлена как Student , тогда как gs.calcTuition( ) обращается к GraduateStudent::calcTuition( ) .
Но что, если класс объекта не может быть точно определён на этапе компиляции? Чтобы продемонстрировать подобную ситуацию, нужно просто немного изменить приведённую выше программу:
//
/* OverloadOverride — демонстрация невозможности */
/* точного определения типа */
//
#include
#include
#include
using namespace std ;
class Student
{
public :
/* Раскомментируйте одну из двух следующих строк; одна выполняет раннее связывание calcTuition( ), а вторая — позднее */
float calcTuition( )
/* virtual float calcTuition( ) */
{
cout << "Функция Student::calcTuition" << endl ;
return 0 ;
}
} ;
class GraduateStudent : public Student
{
public :
float calcTuition( )
{
cout << "Функция GraduateStudent::calcTuition"
<< endl ;
return 0 ;
}
} ;
void fn( Student & x )
{
x.calcTuition( ) ; /* Какая функция calcTuition( ) должна быть вызвана? */
}
_________________
241 стр. Глава 21. Знакомство с виртуальными функциями-членами: настоящие ли они
int main( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */
/* Передача функции объекта базового класса */
Student s ;
fn( s ) ;
/* Передача функции объекта подкласса */
GraduateStudent gs ;
fn( gs ) ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
Данная программа генерирует следующий вывод:
Функция Student::calcTuition
Функция Student::calcTuition
Press any key to continue...
На этот раз вместо прямого вызова calcTuition( ) осуществляется вызов через промежуточную функцию fn( ) . Теперь всё зависит от того, какой аргумент передаётся fn( ) , поскольку х может быть как Student , так и GraduateStudent — ведь GraduateSudent ЯВЛЯЕТСЯ Student !
Читать дальшеИнтервал:
Закладка: