Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие С++- не книжкой_новое.doc
Скачиваний:
4
Добавлен:
04.11.2018
Размер:
765.44 Кб
Скачать

Void empty();

operator int(){return len;}

~CStr(){delete[]s; cout<<" \nDestructor! ";}

char* get_str() const {return s;} int get_len()const {return len;}

friend ostream& operator<<(ostream&,CStr&);

}

// Конструктор создания пустой строки

Str::CStr():len(0)

{s=new char;*s='\0'; cout<<"\nContructor1";}

// Конструктор создания строки, равной заданной С- строке

CStr::CStr(char* a)

{s=new char[len=strlen(a)];

strcpy(s,a);

cout<<"\nContructor2";

}

// Конструктор создания строки из одного символа

CStr::CStr(char a)

{s=new char[len=2];s[0]=a; s[1]='\0';cout<<"\nContructor3";}

// Конструктор копирования

CStr::CStr(const CStr& a)

{s=new char[len=a];strcpy(s,a.s);cout<<"\nContructor4 ";}

// Операция присваивания

CStr& CStr::operator = (const CStr & a)

{

if (&a==this) return *this;

If (len) delete []s;

s=new char [len=a];

strcpy(s,a.s);

cout<<" \nDONE == ";

return *this;

}

// Операция сравнения строк

bool CStr::operator ==(CStr & st)

{

if (strcmp (s, st.s)==0) return true;

return false;

}

// Метод, делающий строку пустой

Void cStr::empty()

{ if (len)

{ len = 0; delete []s; s = new char; *s= '\0';}

}

// Операция записи в поток вывода на экран

ostream& operator<<(ostream& a, CStr& x)

{return a<<x.s;}

Вторым основным принципом ООП является наследование. Наследование – это возможность создания иерархии классов, в которой потомки (производные классы, наследники) получают элементов своих предков (родителей, базовых классов), могут их изменять и добавлять новые.

Синтаксис описания класса-наследника

сlass имя : [ключ доступа] имя базового класса { тело класса};

Ключ доступа может иметь одно из трех значений private, protected, public

Ключ доступа private (защищенное наследование, действует по умолчанию) – понижает статусы доступа public и protected элементов базового класса до private. Ключ доступа public (открытое наследование) - не изменяет статуса доступа элементов базового класса. Ключ доступа рrotected (защищенное наследование) понижает статус доступа public элементов базового класса до protected.

Например, создадим производный класс CBStr от базового класса CStr, предназначенный для хранения бинарных строк.

class CBStr: public CStr

{public:

CBStr();

CBStr(char* a);

CBStr& operator = (const CBStr & );

CBStr operator +( const CBStr& );

void empty();

operator int();

};

Рассмотрим поля и методы производного класса.

Все поля базового класса наследуются.

Если поля родителя имеют тип private, то для работы с ними в классе – наследнике необходимо использовать методы базового класса или объявить их явным образом в наследнике в секции public следующим образом имя базового_класса::имя_поля. Например, если бы поля s, len были бы описаны в классе CStr как private, то в классе CBStr их следует объявить следующим образом:

class CBStr: public CStr

{

….

public:

СStr::s;

СStr::len;

….

};

Кроме того, если функциям производного класса требуется работать с полями базового, то в базовом классе такие поля можно описать как protected, как это и сделано в классе CStr.

Для различных методов класса существует различные методы наследования. Наследуются все методы, кроме конструкторов, деструкторов и операции присваивания.

То есть класс CBStr наследует методы empty(), operator ==, operator int(), get_str(), get_len(), и и дружественную функцию-оператор operator<<;

Однако, как мы видим в классе CBStr методы empty(), operator int() переопределены.

//Метод, делающий строку пустой

void CBStr:: empty()

{ if (len)

{ delete []s;

len = 1;

s = new char[2];

s[0]='0';

s[1]= '\0';

}

}

//Функция-операция преобразования типа, возвращающая десятичное значение двоичной строки

CBStr:: operator int()

{

int k=s[len-1]-48;

int st=2;

for (int i=len-2; i>=0; i--)

{ k+=((s[i]-48)*st); st*=2;}

return k;

}

Кроме того, в классе CBStr определен новый метод

// Операция сложения двух двоичных чисел

CBStr CBStr::operator+( const CBStr& a)

{

int l;

if (len>a.len) l=len; else l=a.len;

char * str =new char[l+2];

itoa (int(*this)+int (a), str, 2);

CBStr S(str);

delete str;

return S;

}

Опредлелим конструкторы класса

Предположим, что создание пустой бинарной строки равносильно созданию обычной строки, состоящей из одного символа ‘0’. Тогда конструктор производного класса должен вызывать конструктор базового класса с параметром ‘0’:

CBStr():CStr('0'){}

Конструктор бинарной строки, равной заданной С-строке, должен вызывать конструктор и если созданная строка, содержит символы отличнее от 0 и 1, делать строку пустой

CBStr::CBStr(char* a):CStr (a){if (!bin(a)) empty();}

{ if (!bin(a)) empty(); }

где bin() – функция проверки С-строки на бинарность, empty () – метод, делающий строку пустой.

bool bin(char *a)

{

int i=0;

while (a[i])

{ if (a[i]!='0' && a[i]!='1') return false;

i++;

}

return true;

}

Конструктор копирования создастся автоматически и вызовет конструктор копирования базового класса.

Для класса CBStr не требуется явным образом создавать деструктор, так как удалить бинарную строку это тоже самое, что и удалить строку (если в производном классе деструктор не определен программистом, то он создастся автоматически компилятором, причем из созданного деструктора будет вызван деструктор базового класса.)

Так как операция присваивания не наследуеются, ее необходимо явным образом переопределить

CBStr& CBStr::operator = (const CBStr & a)

{ CStr :: operator = (a);}

Эффективнее всего работать с объектами одной иерархии через указатели на базовый класс. При открытом наследовании можно присваивать указателю на объект базового класса как адрес объекта базового класса, так и адрес объекта любого производного класса.

Рассмотрим пример работы с объектами одной иерархии через указатели.

CStr a("aaa");

CBStr b("101");

CStr * p1=&a;

CBStr * p2=&b;

cout<<"\na= "<<a <<" "<<int(a);

cout<<"\nb= "<<b <<" "<<int(b);

p1->empty();

p2->empty();

cout<<"\na= "<<a <<" "<<int(a);

cout<<"\nb= "<<b <<" "<<int(b);

CBStr c("1011");

CStr * p3=&c;

cout<<"\nc= "<<c <<" "<<int(c);

p3->empty();

cout<<"\nc= "<<c <<" "<<int(c);

Эта программа выведет на экран

a=aaa 3

b=101 5

a= 0

b= 0

c=1011 11

c= -48

Как мы видим, для объекта, на который ссылается указатель p3, был вызван метод empty() базового класса CStr, что семантически неверно. Таким образом, переопределенный метод empty() производного класса оказался недоступным. Это происходит из-за того, что компилятор не может предсказать на объект какого класса будет фактически ссылается указатель во время выполнения программы и выбирает всегда метод базового класса. Чтобы избежать этой ситуации необходимо объявить в баовом классе метод empty() как виртуальный, то есть со спецификатором virtual.