Перегрузка и переопределение функций
Базовые принципы объектно-ориентированного программирования.
Инкапсуляция – механизм, связывающий данные и код и сохраняющий их от внешнего воздействия и ошибочного использования.
Полиморфизм обеспечивает возможность использования одного кода для решения разных задач. Реализуется ч\з механизм виртуальных функций и перегрузки и переопределения.
Наследование, благодаря которому один объект наследует свойства другого объекта, добавляя к нему свои.
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