Перегрузка и переопределение функций

Базовые принципы объектно-ориентированного программирования.

Инкапсуляция – механизм, связывающий данные и код и сохраняющий их от внешнего воздействия и ошибочного использования.

Полиморфизм обеспечивает возможность использования одного кода для решения разных задач. Реализуется ч\з механизм виртуальных функций и перегрузки и переопределения.

Наследование, благодаря которому один объект наследует свойства другого объекта, добавляя к нему свои.

class A

{

public:

virtual void fun(){ cout << "fun from A" << endl;}

};

class B1:public A

{

public:

void fun(){ cout << "fun from B1" << endl;}

};

class B2:public A

{

public:

void fun(){ cout << "fun from B2" << endl;}

};

int main(){

A *p, obA;

B1 obB1;

B2 obB2;

 

p=&obA; // указатель на объект базового класса

p->fun();

 

p=&obB1; p->fun();

 

p=&obB2; p->fun();

 

return 0;

}

 

Базовые конструкции объектно-ориентированных программ.

Hjj

Конструктор и деструктор. Конструктор по умолчанию.

Конструктор – функция, имя которой совпадает с именем класса, в котором он объявлен и используется. Он ничего не возвращает. К. м.б. без пераметров, с параметрами по умолчанию, с параметрами и копии. Функции к.: выделить память под объект и инициализации (заносит начальные данные).

~Деструктор – метод, приводящий к разрушению объекта соответствующего класса и возвращающий системе область памяти, выделенную конструктором.

class Man

{

public:

char *FIO;

int year;

 

 

Man(char *s = "\0", int y = 0);

void print();

~Man();

};

 

Man ::Man(char *s, int y)

{

int len = strlen(s);

FIO = new char[len + 1];

strcpy(FIO, s);

 

year=y;

 

}

 

Man::~Man()

{

 

delete [] FIO;

}

 

void Man::print()

{

cout<<FIO<<'\t'<<year<<endl;

}

 

int main(){

 

Man ob("a",5);

ob.print();

 

return 0;

}

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

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

class A

{

private:

char *str;

int len;

public:

A();

A(const A&);

~A();

};

 

A::A()

{

len = 20;

str = new char[len + 1];

}

 

A::A(A const &obj1) //copy from obj1 to obj

{

this->len = obj1.len;

str=new char[len+1];

strcpy(str,obj1.str);

}

 

A::~A()

{

delete []str;

}

 

void fun(A obj1)

{

cout<< "qwert" << endl;

}

 

void main()

{

A obj;

fun(obj);

 

}

 

Конструктор explicit.

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

class Mass

{ private:

int n;

int *M;

public:

class Size

{

private:

int count;

public:

Size(int count1):count(count1){}

int size_mass() const {return count;}

};

Mass(int n, int m)

{this->n=n;

M=new int[this->n];

for(int i=0; i < this->n; i++)

M[i] = m;

}

Mass(Size s)

{

n = s.size_mass();

M = new int[n];

}

~Mass()

{

delete []M;

}

};

 

void main()

{

Mass ob(1);

 

}

Компилятор для объекта ob генерирует вызов конструктора Mass(int). Но такого не существует. Компиляторы могут преобразовывать аргумент типа int во временный объект Size, поскольку в классе Size имеется конструктор с одним параметром. Это обеспечивает успешное создание объекта.

_____________________

есть класс stown, у него есть конструктор с одним параметром stown(double k)
затем мы где-нить в программе пишем:
stown myCat;
myCat = 19.333 // и Опа! используется stown(double) для преобразования 19.333 в stown.

а уже с explicit компилятор тебя пошлет! Ффот ))))

Указатель this.

Каждому объявляемому объекту соответствует свой скрытый this-указатель; this м.б. использован только для нестатических функций; this указывает на начало своего объекта в памяти; this не надо дополнительно объявлять; this передаётся как скрытый аргумент во все нестатические компоненты-функции своего объекта: this – локальная переменная и недоступна за пределами объекта.

Пример: Упорядочивание чисел в массиве.

class Mass

{int a[3];

public:

Mass sort();

Mass *input();

void out();

};

Mass Mass::sort()

{ for(int i = 0; i < 2; i++)

for(int j = i; j < 3; j++)

if (a[i]>a[j])

{ a[i] = a[i] + a[j]; a[j] = a[i] - a[j]; a[i] = a[i] - a[j]; }

return *this; //возврат содержимого объекта

}

 

Mass *Mass::input()

{

for (int i = 0; i < 3; i++) cin>>a[i];

return this; //возврат адреса начала объекта

}

 

void Mass::out()

{

cout<<endl;

for(int i = 0; i<3; i++)

cout<< a[i] << ' ';

}

 

void main()

{

Mass o1;

o1.input()->sort().out();

}

Вызывается функция input для ввода инфо в массив объекта o1; эта функция возвращает адрес памяти, где расположен объект o1; вызывается функция сортировки, возвращающая содержимое объекта о1; функция вывода инфо.

 

Абстрактные типы данных.

Абстра́ктный тип да́нных (АТД) — это тип данных, который предоставляет для работы с элементами этого типа определённый набор функций, а также возможность создавать элементы этого типа при помощи специальных функций. Вся внутренняя структура такого типа спрятана от разработчика программного обеспечения — в этом и заключается суть абстракции. Абстрактный тип данных определяет набор независимых от конкретной реализации типа функций для оперирования его значениями. Конкретные реализации АТД называются структурами данных.

В программировании абстрактные типы данных обычно представляются в виде интерфейсов, которые скрывают соответствующие реализации типов. Программисты работают с абстрактными типами данных исключительно через их интерфейсы, поскольку реализация может в будущем измениться. Такой подход соответствует принципу инкапсуляции в объектно-ориентированном программировании. Сильной стороной этой методики является именно сокрытие реализации. Раз вовне опубликован только интерфейс, то пока структура данных поддерживает этот интерфейс, все программы, работающие с заданной структурой абстрактным типом данных, будут продолжать работать. Разработчики структур данных стараются, не меняя внешего интерфейса и семантики функций, постепенно дорабатывать реализации, улучшая алгоритмы по скорости, надежности и используемой памяти.

Различие между абстрактными типами данных и структурами данных, которые реализуют абстрактные типы, можно пояснить на следующем примере. Абстрактный тип данных список может быть реализован при помощи массива или линейного списка, с использованием различных техник динамического выделения памяти. Однако каждая реализация определяет один и тот же набор функций, который должен работать одинаково (по результату, а не по скорости) для всех реализации.

Абстрактные типы данных позволяют достичь модульности программных продуктов и иметь несколько альтернативных взаимозаменяемых реализаций отдельного модуля.

class A

{ private: int a;

public:

void set_a(int num);

int get_a();

};

void A::set_a(int num)

{ a = num;}

int A::get_a()

{ return a;}

void main()

{

A ob1, ob2;

 

ob1.set_a(10);

ob2.set_a(99);

//ob1.a = 10;//ERROR

 

cout << ob1.get_a() << endl;

cout << ob2.get_a() << endl;

}

8. Организация простейшего ввода/вывода в С++.

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

cin [>>имя_переменной]; Объект cin имеет некоторые недостатки. Необходимо, чтобы данные вводились в соответствии с форматом переменных, что не всегда может быть гарантировано.

сout << data [ << data];

data − это переменные, константы, выражения или комбинации всех трех

типов.

Простейший пример применения cout − это вывод, например, символьной

строки:

cout << ”объектно-ориентированное программирование ”;

 

9. Использование манипуляторов для ввода/вывода в С++.

cout << setw(10) << setfill('*') << a << endl;

setw(количество_позиций_для_вывода_числа)

setfil(символ_для_заполнения_пустых_позиций)

setprecision (точность_при_выводе_дробного_числа

int main()

{ int a=0x11, b=4, // целые числа: шестнадцатеричное и десятичное

c=051, d=8, // восьмеричное и десятичное

i,j;

i=a+b;

j=c+d;

cout << i <<' ' <<hex << i <<' '<<oct << i <<' ' <<dec << i <<endl;

cout <<hex << j <<' ' << j <<' '<<dec << j <<' ' << oct << j <<endl;

return 0;

}

#include <iostream>

#include <iomanip>

using namespace std;

int main()

{ int a=0x11;

double d=12.362;

cout << setw(4) << a << endl;

cout << setw(10) << setfill('*') << a << endl;

cout << setw(10 ) << setfill(' ') << setprecision(3) << d << endl;

return 0;

}

Результат работы программы:

********17

12.4

10. Операторы для динамического выделения и освобождения памяти (new и delete).

Различают два типа памяти: статическую и динамическую. В статической

памяти размещаются локальные и глобальные данные при их описании в функ-

циях. Для временного хранения данных в памяти ЭВМ используется динамиче-

ская память, или heap. Размер этой памяти ограничен, и запрос на динамическое

выделение памяти может быть выполнен далеко не всегда.

Для работы с динамической памятью в языке С использовались функции

calloc, malloc, realloc, free и др. В С++ для операций выделения и освобождения

памяти можно также использовать встроенные операторы new и delete.

Оператор new имеет один операнд. Оператор имеет две формы записи:

[::] new [(список_аргументов)] имя_типа [(инициализирующее_значение)]

[::] new [(список_аргументов)] (имя_типа) [(инициализирующее_значение)]

В простейшем виде оператор new можно записать:

new имя_типа или new (имя_типа)

Оператор new возвращает указатель на объект типа «имя_типа», для ко-

торого выполняется выделение памяти. Например:

char *str; // str – указатель на объект типа char

str=new char; // выделение памяти под объект типа char

или

str=new (char);

class A

{ int i; // компонента-данное класса А

public:

A(){} // конструктор класса А

~A(){} // деструктор класса А

};

int main()

{ A *a,*b; // описание указателей на объект класса А

float *c,*d; // описание указателей на элементы типа float

 

a=new A; // выделение памяти для одного объекта класса А

b=new A[3]; // выделение памяти для массива объектов класса А

c=new float; // выделение памяти для одного элемента типа float

d=new float[4]; // выделение памяти для массива элементов типа float

delete a; // освобождение памяти, занимаемой одним объектом

delete [] b; // освобождение памяти, занимаемой массивом объектов

delete c; // освобождение памяти одного элемента типа float

delete [] d; // освобождение памяти массива элементов типа float

}

При удалении объекта оператором delete вначале вызывается деструктор

этого объекта, а потом освобождается память. При удалении массива объектов с

помощью операции delete[] деструктор вызывается для каждого элемента мас-

сива. В качестве аргументов можно использовать как стандартные типы дан-

ных, так и определенные пользователем. В этом случае именем типа будет имя

структуры или класса. Если память не может быть выделена, оператор new воз-

вращает значение NULL.

 

 

Перегрузка и переопределение функций.

Перегруженные функции. Они имеют одинаковые имена, но разное количество и\или типы параметров.

Переопределённые функции. У них совпадает сигнатура (имя + параметры).

Они имею разную реализацию.

class A

{

int i;

public:

A(int ii = 0) {i = ii;}

void show() {cout<<i<<endl;}

};

 

class B:A

{

int j;

public:

B(int ii = 0, int jj = 1):A(ii) {j=jj;}

void show()

{cout<<j<<endl;

A::show();

}

};

 

void main()

{ A obj(5);

obj.show();

 

B obj2(7,4);

obj.show();

}

//На экране будет

5 4 7