Наследование классов в С++
В этом туторе я попытаюсь кратко рассказать про наследование классов в С++ и о том, как избежать самой главной ошибки новиков в кодинге - копирования одинакового кода целиком. 
Итак приступим. Несмотря на то, что наследование классов - это стандартная функция С++, постараюсь рассказать о ней максимально приближенно в кодингу в Half-Life. Если вы захотите узнать о ней вообще - можете почитать соответствующую литературу по языку :) 
Для начала я хочу разобрать самую распространенную ошибку новичков в кодинге - это переписывание необходимых функций целиком. Как же это выглядит на практике ? 
А вот как - хочет бедный ньюб сделать себе новую пушку - ну допустим там, автомат какой. и надо ему в этом автомате - лишь модельку сменить, да и ту, что в руках. а все остальное - должно таким же оставаться. 
Теперь собсно сам процесс - выделяем mp5.cpp ctrl+C, ctrl+V, поменяем ему дефайн на какой нить WEAPON_AUTOMAT - причем перед этим забыв его прописать в weapons.h - компилятор ехидно говорит новичку, что он дурак. 
Начинается усиленный процесс мышления, в ходе которого рождается первый закон программирования на C++ : "функции в одном классе не могут иметь одинаковые имена!" - порой влияние этого закона становиться настолько сильным, что увидев пергрузку функций человек в ужасе удаляет этот код втайне тихо недоумевая почему компилятор не видит столь вопиющего безобразия, но не забывая ругаться, на его собственные, заботливо скопированные файлы\строки - делается вывод о пристрастности компилятора лично к нему, в голову лезут мысли о том, что гадкий мелкософт чего-то там опять намудрил, выпустив такой глючный продукт. 
Твердо усвоив вышеприведенный закон новичок вполне логично выводит, что раз функции не могут иметь одинаковые имена - значит им надо присвоить разные, и чтобы самому не запутаться - каждой функции в имени добавлет по одной букве - например P. Вот и получаются у нас загадочные Mp5p, CgrenadeP и прочие извраты. Любой мало-мальский опытный кодер, глян
ув на этот изврат непременно подумает пару нехороших слов, про "автора" этого кода, а менее воспитанный еще и скопирует эти слова из мозга в голосовой буфер, чтобы воспроизвести все это в адрес новичка :) 
Но как я уже сказал, влиянее первого закона + страх перед компилятором + извечное программерское "если это работает - лучше я не буду ничего трогать" создают в мозгу новичка непреодолимый психологический барьер, преодолеть который для многих, порой - невыполнимая задача. Вот и кодинг для них в основном сводиться переименовыванию имен функций... 
Это у нас была теория с небольшим отклоненением в психологию, теперь перейдем непосредственно к практике, и пусть знания, полученные вами из этого тутора станут первым шагом на пути к написанию кода с нуля :) 
Для начала рассмотрим само понятие "наследование классов". Я не изучал подробно какие-либо языки кроме асма и С++, а поэтому не знаю есть ли классы в том же визуал бейсике или делфи (скорее всего нету). Поэтому предполагаю, что вы с этим понятием столкнулись впервые и не знаете, что оно из себя представляет. Итак наследование классов - это иерархия. Легче все представить себе это графически. Откройте файл cbase.h (лучше всего из спирита - там нагляднее, но и обычная халфа тоже подойдет). Вот собсно как все выглядит.  
прим. G-Cont. иерархия на данный момент дана для спирита 1.5 CB 
Class Hierachy на самом деле - это не класс, просто для удобства. 
Давайте подробно рассмортим, какой класс подчиняется какому и почему было сделано именно так, а не иначе. 
Как видно из рисунка - главным классом является CBaseEntity - из него собсно "произрастают" все остальные. Именно в классе CBaseEntity нахоядтся всем знакомые pev->skin, pev->body, pev->sequence и прочие параметры. (если быть точным, то они входят в глобальную структуру entvars_t, но я не хочу сильно усложнять материал). 
Как вы знаете - все эти pev->skin, pev->body, pev->sequence доступны из любой энтити и компилятор вам не скажет, что он видит эту переменную первый раз в жизни. 
Далее на одном и том же уровне идут CPointEntity и CBaseDelay, чисто от самой pointentity можно добиться не так уж и много - зачастую, их юзают просто для указания координат, например как info_player_start. 
CBaseDelay - мощная штука - из него практически вырастает все, что есть на сервере. Как видите оттуда идут патроны и итемы игрока, а также его оружие. 
Из CBaseToggle у нас получаються основные брашевые энтити - всякие двери и поезда, а из CBaseMonster - даже сам игрок :) Ведь игрок по сути дела - тоже монстр, только управлемый геймером :). 
Данная иерархия очень упрощенная и не содержит ВСЕХ классов сервера, она нужна, просто для понятия кодером что кому принадлежит. так допустим, если это CBaseToggle, то в его класс входят всякие функции для перемещения брашевых энтитей. А для оружия - всякие там функции добавления - удаления патронов. 
Ессно, что чем глубже класс - тем больше в его распоряжении всяких функций от надклассов, которые даже не нужно вызывать - они уже готовы к использованию. Ну а теперь вернемся к пресловутому примеру с оружием. 
Вот два рисунка. Задача следующая: добавить новую пушку, у которой требуется поменть только лишь модельку и кол-во патронов.  
Собственно это и есть основная идея наследования классов. 
Хотим создать новую пушку на основе старой, заменив модель ? нет проблем :) 
class Cnewweapon : public CMP5 и мы имеем в своем распоряжении ВСЕ функции MP5, причем нам не надо их даже декларировать! 
Что у нас изменилось ? v_ моделька пушки ? в каких функциях она у нас прописана ? Замечательно - в Precache и в deploy. декларируем две эти функции. В прeкеше можно просто прекешить нашу новую модельку и не писать более ничего - так или иначе, все остальное уже прекешировано в Mp5 :) 
Теперь пишем коротенькую функцию deploy, сменив там модельку автомата на свою. Ну и ессно нужно добавить LINK_ENTITY_TO_CLASS - для нашей новой пушки. 
Что-то еще ? ах да - информация об оружии. 
int CnewWeapon::GetItemInfo(ItemInfo *p) 
{ 
p->pszName = STRING(pev->classname); 
p->pszAmmo1 = "9mm";//пишете хоть ракеты - это влияет только на тип добавляемых боеприпасов 
p->iMaxAmmo1 = _9MM_MAX_CARRY;//сколько патронов может вместить пушка 
p->pszAmmo2 = NULL; 
p->iMaxAmmo2 = -1; 
p->iMaxClip = GLOCK_MAX_CLIP;//размер клипа 
p->iSlot = 1; 
p->iPosition = 0; 
p->iFlags = 0; 
p->iId = m_iId = WEAPON_AUTOMAT;//не забудьте продефайнить в weapons.h ;) 
p->iWeight = GLOCK_WEIGHT;// а это можно вообще не трогать :) 
return 1; 
} 
Ессно все добавлемые функции нужно внести в класс новой пушки :) 
Ну вот мы и получили пушку, код которой весит в 6 раз меньше оригинала (можете даже вставить этот код в mp5.cpp, если не хотите создавать отдельный файл, которая обладает всеми свойствами Mp5, и все же является совершенно другим, самостоятельным оружием :) 
Я специально рассмотрел пример с оружием, но вам не составит никакого труда также добавить нового монстра - например отиса, сделав его из барни :) 
Ну вот вроде бы и все на сегодня - надеюсь я понятно объяснил материал. 
Надеюсь что эти 8 с лишним килобайт текста пойдут вам на пользу. 
Пишите комментарии отзывы и вопросы, если вдруг что непонятно :) 
g-cont