Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МВ_Прог_1курс_2n1часть.doc
Скачиваний:
11
Добавлен:
13.04.2015
Размер:
735.74 Кб
Скачать

Операции new и delete

Объект размещается в динамической памяти при помощи операции new.

date* p = new date(19, 10, 2001);

Если выделить память не удалось, то по новому стандарту возбуждается исключение, по старому – возвращается 0.

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

Удаляется объект из динамической памяти операцией delete.

delete p;

Дважды удалять из динамической памяти один и тот же объект нельзя!

Статические элементы класса

С помощью модификатора static можно описать статические поля и методы класса. Их можно рассматривать как глобальные переменные или функции, доступные только в пределах области класса.

Статические поля

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

Эти поля существуют для всех объектов класса в единственном экземпляре, то есть не дублируются.

Особенности статических полей.

- Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов.

- Статические поля доступны как через имя класса, так и через имя объекта:

На статические поля распространяется действие спецификаторов доступа

Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof.

class A{

public:

static int cout;

};

int A::cout;

A b,*c;

int main()

{

cout<<A::cout<<endl;//10

cout<<b.cout<<endl;//10

cout<<c->cout<<endl;//10

A::cout=100;

cout<<A::cout<<endl;//10

cout<<b.cout<<endl;//10

cout<<c->cout<<endl;//10

/////////////////////////////

cin.get();

return 0;

}

//---------------------------------------------------------------------------

Статические методы

Статические методы предназначены для обращения к статическим полям класса. Они могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, поскольку в им не передается скрытый указатель this. Обращение к статическим методам производится так же, как к статическим полям — либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.

#include <iostream>

using namespace std;

class B{

static int cout;

int data;

public:

static void inc_cout(){cout++;}

static int get_cout(){return cout;}

};

int B::cout(13);

int main()

{

B d,*e;

cout<<d.get_cout()<<endl;

d.inc_cout();

cout<<e->get_cout()<<endl;

cin.get();

return 0;

}

//---------------------------------------------------------------------------

Статические методы не могут быть константными (const) и виртуальными (virtual).

Дружественные функции и классы

Иногда желательно иметь непосредственный доступ извне к скрытым полям класса, то есть расширить интерфейс класса. Для этого служат дружественные функции и дружественные классы.

Дружественная функция

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

Правила описания и особенности дружественных функций.

- Дружественная функция объявляется внутри класса, к элементам которого ей нужен доступ, с ключевым словом friend.

- В качестве параметра ей должен передаваться объект или ссылка на объект класса, поскольку указатель this ей не передается.

- Дружественная функция может быть обычной функцией или методом другого ранее определенного класса.

- Одна функция может быть дружественной сразу нескольким классами.

- Использования дружественных функций нужно по возможности избегать.

Дружественный класс

Если все методы какого-либо класса должны иметь доступ к скрытым полям другого, весь класс объявляется дружественным с помощью ключевого слова friend.

#include <iostream>

using namespace std;

class TA{

int a;

int b;

public:

int d;

TA (int x=3,int y=4){a=x;b=y;}

friend void print(TA&);

friend class TB;

};

class TB{

public:

void inc_TA(TA& inca){inca.a++;};

void inc_TB(TA& incb){incb.b++;};

};

void print(TA &obj)

{cout<<obj.a<<"\t"<<obj.b<<endl;}

int main()

{

TA b,*c,d(4,5),*z;

c=new TA();

z=new TA(7,8);

print(b);

print(d); //4 5

print(*c);

print(*z);

delete c;

delete z;

TB q;

q.inc_TA(d);

q.inc_TB(d);

print(d);//5 6

cin.get();

return 0;

}

Объявление friend не является спецификатором доступа и не наследуется.

Деструкторы

Деструктор — это особый вид метода, применяющийся для освобождения памяти, занимаемой объектом. Деструктор вызывается автоматически, когда объект выходит из области видимости:

□ для локальных объектов — при выходе из блока, в котором они объявлены;

□ для глобальных — как часть процедуры выхода из main:

□ для объектов, заданных через указатели, деструктор вызывается неявно при использовании операции delete.

Имя деструктора начинается с тильды (-), непосредственно за которой следует имя класса. Деструктор:

□ не имеет аргументов и возвращаемого значения;

□ не может быть объявлен как const или static;

□ не наследуется;

может быть виртуальным .

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

Описывать в классе деструктор явным образом требуется в случае, когда объект содержит указатели на память, выделяемую динамически — иначе при уничтожении объекта память, на которую ссылались его поля-указатели, не будет помечена как свободная.

Указатель на деструктор определить нельзя.

Деструктор можно вызвать явным образом путем указания полностью уточненного имени:

Это может понадобиться для объектов, которым с помощью перегруженной операции new выделялся конкретный адрес памяти. Без необходимости явно вызывать деструктор объекта не рекомендуется.

Перегрузка операций

C++ позволяет переопределить действие большинства операций так, чтобы при использовании с объектами конкретного класса они выполняли заданные функции.

Можно перегружать любые операции, существующие в C++, за исключением:

. .* ?: :: # ## sizeof

Перегрузка операций осуществляется с помощью методов специального вида (функций-операций) и подчиняется следующим правилам:

□ при перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных типах данных;

□ для стандартных типов данных переопределять операции нельзя;

□ функции-операции не могут иметь аргументов по умолчанию;

□ функции-операции наследуются (за исключением -);

□ функции-операции не могут определяться как static.

Функцию-операцию можно определить тремя способами: она должна быть либо методом класса, либо дружественной функцией класса, либо обычной функцией.

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

Функция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции:

тип operator операция ( список параметров) { тело функции }

Перегрузка унарных операций

#include <iostream>

using namespace std;

class TA{

int a;

int b;

public:

TA (int x=3,int y=4){a=x;b=y;}

friend void print(TA&);

//перегрузка унарной операции

/*Унарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода без параметров, при этом операндом является вызвавший ее объект, например:

*/

TA& operator++(){a++;b++;return *this;}

//перегрузка бинарной операции : вызывающий объект считается первым операндом

const TA& operator=(const TA& operand2)

{if(&operand2==this) return *this;

//если класс содержит динамический массив необходимо удалить

//занимаемую память и выделить новую память нужного размера

//затем скопировать все элементы класса аналогично конструктору копирования

a=operand2.a;b=operand2.b;

}

TA operator+(TA& operand2){

TA sum;

sum.a=a+operand2.a;

sum.b=b+operand2.b;

return sum;

}

bool operator>(TA& operand2){

return (a>operand2.a&&b>operand2.b)? true: false;

}

//перегрузка потоков ввода вывода

//перегрузка потоков осуществляется только

//с помощью дружественных функций

friend ostream& operator<<(ostream& out,TA &obj)

{out<<"a= "<<obj.a<<"\tb="<<obj.b;

return out;}

friend istream& operator>>(istream& in,TA &obj)

{

cout<<"Vvedite a=";

in>>obj.a;

cout<<"Vvedite b=";

in>>obj.b;

return in;}

};

int main()

{

TA b(4,5),c(1,1);

print(b);

b++;

print(b);

TA s=b+c;

print(s);

if (s>b)cout<<">"<<endl;

cin>>s;

cout<<s<<endl;//a=6 b=7

cin.get();

cin.get();

retrun 0;

}

//---------------------------------------------------------------------------

void print(TA &obj)

{cout<<obj.a<<"\t"<<obj.b<<endl;}

/*

Написать класс "Вектор"

Реализовать конструкторы по уполчанию, с параметром и коппирования

Релизовать в виде методов

- заполнения массива случайным образом;

- суммирование элементов массива

Перегрузить операции:

- сравнения (возвращает истину, если сумма элементов обоих операндов равна);

- присваивания;

- потоковые операции ввода (">>") вывода "<<".

Проиллюстрировать работу реализованных методов.

*/

#include "stdafx.h"

#include <iostream>

#include <time.h>

class Tvec

{

int *vec;

int n;

public:

Tvec();//конструктор по умолчанию

Tvec(int n);//конструктор с параметром

Tvec(Tvec &);//конструктор коппирования

~Tvec(){delete [] vec;}//деструктор

void Rand(int);//метод заполнения случайным образом от 0 до заданного параметра

int Sum();//метод суммирования элементов

bool operator==(Tvec&);//перегрузка операции "=="

Tvec& operator=(Tvec&);//перегрузка операции "="

Tvec operator+(Tvec&);//перегрузка операции "+"

//перегрузка потоков

friend std::istream& operator>>(std::istream&,Tvec&);

friend std::ostream& operator<<(std::ostream&,Tvec&);

};

int main()

{

Tvec vec1, vec2(10);

vec2.Rand(5);

vec1.Rand(10);

std::cout<<"Ischodnie dannie:"<<std::endl;

std::cout<<"vec1 = "<<vec1<<std::endl;

std::cout<<"vec2 = "<<vec2<<std::endl;

std::cout<<"Sum1 = "<<vec1.Sum()<<std::endl;

std::cout<<"Sum2 = "<<vec2.Sum()<<std::endl;

if( vec1==vec2) std::cout<<"vec1==vec2"<<std::endl;

else std::cout<<"vec1!=vec2"<<std::endl;

Tvec vec3(vec2);

if( vec3==vec2) std::cout<<"vec3==vec2"<<std::endl;

else std::cout<<"vec3!=vec2"<<std::endl;

vec1=vec2;

std::cout<<"Vec1 = "<<vec1<<std::endl;

vec3.Rand(8);

std::cout<<"Vec3 = "<<vec3<<std::endl;

std::cout<<"Vec1+Vec3 = "<<vec1+vec3<<std::endl;

std::cin.get();

return 0;

}

Tvec::Tvec()

{ this->n=5;

vec=new int[n];

}

Tvec::Tvec(int n=5)

{ this->n=n;

vec=new int[n];

}

Tvec::Tvec(Tvec &obj)

{this->n=obj.n; vec=new int[n];

for(int i(0);i<n;i++)

vec[i]=obj.vec[i];

};

void Tvec::Rand(int k)

{

srand(time(NULL));

for(int i(0);i<n;i++)

vec[i]=rand()%k;

}

int Tvec::Sum()

{

int S(0);

for(int i(0);i<n;i++)

S+=vec[i];

return S;

}

bool Tvec::operator==(Tvec& obj)

{

int S1=this->Sum();

int S2=obj.Sum();

return (S1==S2);

}

Tvec& Tvec::operator=(Tvec& obj)

{

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

this->~Tvec();

n=obj.n;

vec=new int[n];

for(int i(0);i<n;i++)

vec[i]=obj.vec[i];

return *this;

}

Tvec Tvec::operator+(Tvec& obj)

{

int m=n+obj.n;

Tvec Merg(m);

int i(0);

for(;i<n;i++)

Merg.vec[i]=vec[i];

for(;i<m;i++)

Merg.vec[i]=obj.vec[i-n];

return Merg;

}

std::istream& operator>>(std::istream& in,Tvec& obj)

{

for(int i(0);i<obj.n;i++)

in>>obj.vec[i];

return in;

}

std::ostream& operator<<(std::ostream& out,Tvec& obj)

{

for(int i(0);i<obj.n;i++)

out<< obj.vec[i]<<" ";

out<<'\n';

return out;

}

Пример выполнения программы

Ischodnie dannie:

vec1 = 7 4 8 5 3

vec2 = 2 4 3 0 3 4 0 1 3 1

Sum1 = 27

Sum2 = 21

vec1!=vec2

vec3==vec2

Vec1 = 2 4 3 0 3 4 0 1 3 1

Vec3 = 1 0 4 1 1 7 1 7 4 3

Vec1+Vec3 = 2 4 3 0 3 4 0 1 3 1 1 0 4 1 1 7 1 7 4 3