Археология об основании Рима: Новые раскопки проясняют и такой острый дискуссионный вопрос, как дата самого возникновения Рима...
Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...
Топ:
Процедура выполнения команд. Рабочий цикл процессора: Функционирование процессора в основном состоит из повторяющихся рабочих циклов, каждый из которых соответствует...
Когда производится ограждение поезда, остановившегося на перегоне: Во всех случаях немедленно должно быть ограждено место препятствия для движения поездов на смежном пути двухпутного...
Методика измерений сопротивления растеканию тока анодного заземления: Анодный заземлитель (анод) – проводник, погруженный в электролитическую среду (грунт, раствор электролита) и подключенный к положительному...
Интересное:
Лечение прогрессирующих форм рака: Одним из наиболее важных достижений экспериментальной химиотерапии опухолей, начатой в 60-х и реализованной в 70-х годах, является...
Что нужно делать при лейкемии: Прежде всего, необходимо выяснить, не страдаете ли вы каким-либо душевным недугом...
Отражение на счетах бухгалтерского учета процесса приобретения: Процесс заготовления представляет систему экономических событий, включающих приобретение организацией у поставщиков сырья...
Дисциплины:
2017-05-23 | 601 |
5.00
из
|
Заказать работу |
Содержание книги
Поиск на нашем сайте
|
|
Лекция №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;}
void Get (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 int count;
};
int Any:: count =0;
К статическим данным-членам, объявленным в разделе public класса, рекомендуется обращаться с помощью следующей конструкции:
<имя_класса>::<данное_член>
Эта форма обращения отражает тот факт, что соответствующее данное-член является единственным для всего класса.
Если статические данные-члены объявлены как закрытые, то доступ к ним можно получить с помощью обычных функций-членов. Доступ к статическим данным-членам с помощью обычных функций-членов ничем не отличается от доступа к другим данным-членам, но для этого необходимо создать хотя бы один объект данного класса. В связи со сказанным выше, можно дать следующие рекомендации:
1. Применяйте статические данные-члены для совместного использования данных несколькими объектами класса;
2. Ограничьте доступ к статическим данным-членам, объявив их в разделе protected или private.
Пример 6.
Файл Dot.h
class Dot
{
static int count;// объявление статического данного-члена – счётчика объектов
char name;
int x, y;
public:
Dot (char Name) { name = Name; x =0; y =0; count ++;}
~ Dot () { count --;}
|
void PrintCount (); // функция выводит количество существующих объектов
};
Файл Dot.cpp
#include " Dot. h "
void Dot:: PrintCount ()
{
char S [30];
CharToOem ("В памяти существует ", S);
cout << S << count;
CharToOem (" объекта типа Dot\n", S);
cout << S;
}
Файл Main.cpp
#include " Dot. h "
int Dot:: count =0; // инициализация статического данного-члена
int main ()
{
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
{
int x, 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 "
void Dot:: PrintCount ()
{
char T [30];
CharToOem ("В памяти существует ", T);
cout << T << count;
CharToOem (" объекта типа Dot\n", T);
cout << T;
}
Файл Main.cpp
#include " Dot. h "
int Dot:: count =0; // инициализация статического данного-члена
int main ()
{
char S [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 p 1(6, 9); // константный объект
int a, b;
cin >> a >> b;
p. GetVal (a, b);
p 1. SetVal (a, b); // Ошибка!!! Вызов неконстантной функции-члена
p 1. 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;}
};
int main ()
{
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-ФУНКЦИИ.
|
|
Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...
Индивидуальные и групповые автопоилки: для животных. Схемы и конструкции...
Биохимия спиртового брожения: Основу технологии получения пива составляет спиртовое брожение, - при котором сахар превращается...
Наброски и зарисовки растений, плодов, цветов: Освоить конструктивное построение структуры дерева через зарисовки отдельных деревьев, группы деревьев...
© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!