Указатель this. Inline-функции.

Лекция №4

УКАЗАТЕЛЬ THIS. INLINE-ФУНКЦИИ.

КОНСТАНТНЫЕ И СТАТИЧЕСКИЕ ДАННЫЕ И ФУНКЦИИ.

Указатель this

Каждый объект в C++ содержит специальный указатель с именем this, который автоматически создается самим компилятором и указывает на текущий объект. Типом this является Т*, где Т – тип класса текущего объекта. Поскольку указатель this определен в классе, область его действия – класс, в котором он определен. Фактически this является скрытым параметром класса, добавляемым самим компилятором к его определению. При вызове обычной функции-члена класса ей передается указатель this так, как если бы он был первым аргументом. Таким образом, вызов функции-члена

ObjName.FuncName(par1, par2);

компилятор трактует так:

ObjName.FuncName(&ObjName, parl, par2);

Но, поскольку аргументы помещаются в стек справа налево, указатель this помещается в него последним. В теле функции-члена адрес объекта доступен как указатель this. Дружественным функциям и статическим функциям-членам класса указатель this не передается. Нижеследующие примеры демонстрируют использование этого указателя:

Пример 1.

#include<iostream.h>

#include<string.h>

class Prim

{

public:

Prim(char*);

void Privet();

char metka[20];

};

#include "Prim.h"

Prim::Prim(char* name)

{

strcpy(metka, name);

Privet(); // Все три

this->Privet(); // оператора

(*this).Privet(); // эквивалентны

}

void Prim::Privet()

{

cout<<"Hello, "<<metka<<endl; // Оба оператора

cout<<"Hello, "<<this->metka<<endl; // эквивалентны

}

int main()

{

Prim ob("dear!");

}

Пример 2.

// функция возвращает точку − середину отрезка, концы которого заданы

Dot& Dot::Middle(Dot A, Dot B)

{

x=(A.x+B.x)/2.0; y=(A.y+B.y)/2.0; // вычисляет середину отрезка

Print(); // выводит на экран координаты текущей точки

this->Print(); // все три оператора эквивалентны

(*this).Print();

return *this; // возвращает ссылку на текущую точку

}

Как можно видеть, внутри функции-члена Middle(Dot A, Dot B) обращения к данным-членам класса и функциям-членам могут осуществляться как непосредственно по имени, так и с помощью указателя this.

На практике такое употребление указателя this встречается крайне редко. В основном указатель this используется для возврата указателя (в форме: return this;) или ссылки (в форме: return *this;) на соответствующий объект. Этот указатель находит широкое применение при перегрузке операторов.

Встраиваемые (inline) функции

В C++ можно задать функцию, которая, фактически, не вызывается, а ее тело встраивается в программу в месте ее вызова. Она действует почти так же, как макроопределение с параметрами. По сравнению с обычными функциями встраиваемые (inline) функции обладают тем преимуществом, что их вызов не связан с передачей аргументов и возвратом результатов через стек и, следовательно, они выполняются быстрее обычных. Недостатком встраиваемых функций является то, что если они слишком большие и вызываются слишком часто, объем программы сильно возрастает. Из-за этого применение встраиваемых функций обычно ограничивается только очень простыми функциями.

Объявление встраиваемой функции осуществляется с помощью спецификатора inline, который вписывается перед определением функции.

Следует иметь в виду, что спецификатор inline только формулирует требование компилятору сформировать встроенную функцию. Если компилятор не в состоянии выполнить это требование, функция компилируется как обычная.

Компилятор не может сгенерировать функцию как встраиваемую, если она:

§ содержит оператор цикла (for, while, do-while);

§ содержит оператор switch или goto;

§ содержит статическую переменную (static);

§ если функция является рекурсивной;

§ имеет возвращаемый тип, отличный от void, и не содержит оператора return;

§ содержит встроенный код ассемблера.

Компилятор может налагать и другие ограничения на использование inline-функции, которые можно уточнить в описании конкретного компилятора. Ниже приведен пример использования встраиваемой функции:

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

Пример 3.

inline int chet(int x) {return !(x%2);}

int main()

{

int n;

cin>>n;

if(chet(n)) cout<<"Chetnoe\n";

else cout<<"Nechetnoe\n";

}

В этом примере используется встраиваемая функция для проверки числа на четность.

Встроенными могут быть объявлены не только обычные функции, но и функции-члены. Для этого достаточно перед определением функции вставить ключевое слово inline или включить ее определение в объявление класса (в этом случае ключевое слово inline больше не нужно).

Пример 4.

Файл Dot.h

class Dot // класс точки

{

double x, y;

public:

// объявление и определение встраиваемых функций

inline double GetX(){return x;}

inline double GetY(){return y;}

voidGet(double X, double Y) {x=X; y=Y;}

// объявление функций

void SetX(double X);

void SetY(double Y);

};

// определение функций вне тела класса, как встраиваемых

inline void Dot::SetX(double X) {x=X;}

inline void Dot::SetY(double Y) {x=Y;}

Пример 5.

class Any

{

public:

Any();

static intcount;

};

intAny::count=0;

К статическим данным-членам, объявленным в разделе public класса, рекомендуется обращаться с помощью следующей конструкции:

<имя_класса>::<данное_член>

Эта форма обращения отражает тот факт, что соответствующее данное-член является единственным для всего класса.

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

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

2. Ограничьте доступ к статическим данным-членам, объявив их в разделе protected или private.

Пример 6.

Файл Dot.h

class Dot

{

static int count;// объявление статического данного-члена – счётчика объектов

charname;

int x, y;

public:

Dot (char Name) {name=Name; x=0; y=0; count++;}

~Dot() {count--;}

void PrintCount(); // функция выводит количество существующих объектов

};

Файл Dot.cpp

#include "Dot.h"

voidDot::PrintCount()

{

charS[30];

CharToOem("В памяти существует ", S);

cout<<S<<count;

CharToOem(" объекта типа Dot\n", S);

cout<<S;

}

Файл Main.cpp

#include "Dot.h"

int Dot::count=0; // инициализация статического данного-члена

intmain()

{

Dot A('A'), B('B'), C('C');

A.PrintCount();

cout<<Dot::count<<'\n'; // ошибка: данное-член count недоступен

}

В этом примере статическое данное-член count содержит количество существующих объектов. Инициализация данного-члена count осуществляется перед выполнением программы и созданием объектов в файле основной программы с помощью оператора: void Dot::PrintCount()

Обратите внимание, что обращение к этому члену класса в функции main() в операторе cout<<Dot::count<<'\n'; приводит к ошибке, поскольку данное-член класса count объявлено как закрытое и является недоступным за пределами класса.

При выполнении программа выводит на экран:

Пример 7.

Файл Dot.h

class Dot

{

intx,y;

static int count;

public:

Dot () {x=0; y=0; count++;}

~Dot() {count--;}

static int GetCount() {return count;}

void PrintCount();

};

Файл Dot.cpp

#include "Dot.h"

voidDot::PrintCount()

{

charT[30];

CharToOem("В памяти существует ", T);

cout<<T<<count;

CharToOem(" объекта типа Dot\n", T);

cout<<T;

}

Файл Main.cpp

#include "Dot.h"

int Dot::count=0; // инициализация статического данного-члена

intmain()

{

charS[25];

CharToOem("Сейчас есть объектов: ", S);

cout<<S<<Dot:: GetCount()<<'\n';

Dot A, B, C;

A.PrintCount();

cout<<Dot:: GetCount()<<'\n';

}

Пример 8.

class Dot // класс точки

{

const char name; // имя точки – константное данное-член

double x, y; // координаты точки

public: // открытые члены класса

Dot(char Name, double X, double Y): name(Name), x(X), y(Y) {}

// или так Dot(char Name, double X, double Y): name(Name) { x=X; y=Y;}

Dot(char Name): name(Name) {x=0; y=0;}

};

Пример 9.

class Coord // базовый класс координат

{

double x, y;

public:

Coord() {x=0; y=0;}

double GetX() const {return x;} // константная функция

double GetY() const {return y;} // константная функция

void GetVal(double X, double Y) const; // константная функция

void SetX(double X) {x=X;}

void SetY(double Y) {y=Y;}

};

Константные объекты

Можно также создавать константные объекты. Для этого их объявления предваряют модификатором const. Например,

const Coord A(3, 5);

Ключевое слово const информирует компилятор, что состояние данного объекта не должно изменяться. В связи с этим компилятор генерирует сообщение об ошибке, если для константного объекта вызывается функция-член (которая может изменить его данные-члены, изменив тем самым его состояние). Исключением из этого правила являются константные функции-члены, которые в силу своего определения не изменяют состояние объекта.

Пример 10.

class Coord // базовый класс координат

{

int x, y;

public:

Coord(int X, int Y) {x=X; y=Y;}

void SetVal(int X, int Y) {x=X; y=Y;}

void GetVal(double &X, double &Y) const; // константная функция

};

Coord::GetVal(double &X, double &Y) const

{

X=x; Y=y;

}

int main()

{

Coord p(3, 8);

const Coord p1(6, 9); // константный объект

int a, b;

cin>>a>>b;

p.GetVal(a, b);

p1.SetVal(a, b); // Ошибка!!! Вызов неконстантной функции-члена

p1.GetVal(a, b);

}

Чтобы обойти указанные ограничения на использование константных функций-членов класса, в стандарт языка С++ было введено новое ключевое слово mutable. Это ключевое слово позволяет указать, какие данные-члены класса могут быть модифицированы константными функциями-членами. Ключевое слово mutable нельзя использовать для статических и константных членов-данных; оно используется как модификатор типа, то есть синтаксис его использования имеет вид:

mutable <тип_данных> <имя_переменной-члена>;

Пример 11.

class AnyClass

{

mutable int count;

mutable const int *iptr;

public:

int func(int i=0) const

{

count=i++;

iptr=&i;

cout<<iptr;

return count;

}

};

Здесь в операторе mutable const int*iptr; модификатор mutable допустим, так как iptr является указателем на целое число, которое есть константа, хотя сам указатель константой не является.

Массивы объектов класса

Из объектов класса, как и из обычных переменных, можно строить массивы. Синтаксис объявления массива объектов аналогичен синтаксису объявления массивов обычных переменных. Например, следующее объявление создает массив из 10 элементов, которые являются объектами класса AnyClass:

AnyClass obArr[10];

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

Доступ к элементам массива также аналогичен доступу к массивам переменных любого другого типа. Например:

#include <iostream.h>

class AnyClass

{

int a;

public:

AnyClass(int n){a=n;}

int GetA(){return a;}

};

int main()

{

//Объявление и инициализация массива объектов

AnyClass obArr[5]={13, 17, 21, 23, 27};

int i;

for(i=0;i<5;i++)

{

//Обращение к элементам массива

cout<<obArr[i].GetA()<<' ';

}

}

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

AnyClass obArr[5] = {AnyClass(13), AnyClass(17), AnyClass(21),

AnyClass(23), AnyClass(27)};

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

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

#include <iostream.h>

class Coord

{

int x, у;

public:

Coord(int X, int Y){x=X; у=Y;}

int GetX(){return x;}

int GetY(){return y;}

};

int main()

{

Coord coordArr[4][2] ={Coord(3,4), Coord(5,6),

Coord(7,8), Coord(9,10),

Coord(ll,12), Coord(13,14),

Coord(15,16), Coord(17,18)};

int i, j;

for(i=0; i<4;i++)

{

for(j=0; j<2; j++)

{

cout<<coordArr[i][j].GetX()<<' ';

cout<<coordArr[i][j].GetY()<<' ';

}

cout<<endl;

}

}

Использование указателей для доступа к объектам массива совершенно аналогично их использованию для обычных переменных и структур. Арифметика указателей также аналогична. Инкрементирование указателя приводит к тому, что он указывает на следующий объект массива, декрементирование − к тому, что он указывает на предыдущий объект массива. Рассмотрим пример:

#include <iostream.h>

class Coord

{

int x, у;

public:

Coord(int X, int Y){x=X; у=Y;}

int GetX(){return x;}

int GetY(){return y;}

}

int main()

{

Coord obArr[4][2]={Coord(3,4), Coord(5,6),

Coord(7,8), Coord(9,10),

Coord(11,12), Coord(13,14),

Coord(15,16), Coord(17,18)};

int i;

Coord *ptr;

ptr=obArr; //Инициализация указателя адресом массива

for(i=0; i<4; i++)

{

cout<<ptr->GetX()<<' ';

cout<<ptr->GetY()<<"\n";

ptr++; //Переход на следующий объект

}

}

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

Массивы объектов классов могут располагаться в куче. Например:

#include <iostream.h>

class Coord

{

int x, у;

public:

Coord(int X, int Y){x=X; у=Y;}

Coord(){x=0; у=0;}

int GetX() {return x;}

int GetY() {return y;}

};

intmain()

{

int i;

Coord *ptr;

ptr = new Coord[6]; //Создание массива объектов в куче

for (i=0; i<6; i++ )

{

cout<<ptr->GetX()<<' ';

cout<<ptr->GetY()<<"\n";

ptr++ ; //Переход на следующий объект

}

cout<<"\n";

delete[] ptr; //Удаление массива объектов из кучи

}

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

Лекция №4

УКАЗАТЕЛЬ THIS. INLINE-ФУНКЦИИ.

Наши рекомендации