Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Поточний об'єкт this, указники на члени класу і класні функції, порівняння з указниками на (позакласні) функції, указники на статичні члени класу




Поточний об'єкт this, указники на члени класу і класні функції, порівняння з указниками на (позакласні) функції, указники на статичні члени класу

 

Тепер подивимося, як методи дістаються до своїх атрибутів. Наприклад, визначимо

два вікна

 

const Screen w(4,3,"aaaabbbbcccc");

Screen u(w);

 

 

кожен з них змінить значення свого курсору, так що присвоєння

 

_cursor = _width*i + j;

 

застосовується до свого атрибуту:

 

w._cursor == 11

а

 

u._cursor == 14.

Як бачимо, метод, активізований об'єктом, повинен знати, який саме об'єкт,

його активізував. Для цього існує указник поточного об'єкту this, його тип

Screen*. Отже повний текст будь-якого методу, наприклад, методу move мав би

наступний вигляд

 

void Screen::move(int i, int j) const

{

if ((i>=this->_height) || (j>=this->_width))

this->_cursor=0;

else

this->_cursor = this->_width*i + j;

};

 

або для копіювального конструктора

Screen::Screen(const Screen& v)

{

this->_filler = v._filler;

this->_height = v._height;

this->_width = v._width;

this->_wContent = new char [_height*_width+1];

strcpy(this->_wContent,v._wContent);

this->_cursor=v._cursor;

}

 

 

Ясно, що в усіх цих випадках діє правило замовчування: “безпритульні” атрибути

і методи приписуються поточному об'єкту. Але легко уявити собі ситуацію, коли

явне посилання на поточний об'єкт стає необхідним. Як приклад розглянемо копіювання

параметру до поточного об'єкту

 

void Screen::copy(const Screen& v)

{

if (this!= &v)

{

delete []_wContent;

_height = v._height;

_width = v._width;

_wContent = new char [_height*_width+1];

strcpy(_wContent,v._wContent);

_cursor=v._cursor;

}

};

 

Копіюванню передує перевірка на збіг об'єкту, що копіюється, з цільовим об'єктом.

Якщо такої перевірки не зробити, то доведеться виконувати зайві і, власне,

не цілком коректні дії.

 

Інше важливе застосування указника на поточний об'єкт виникає при необхідності

повернення поточного об'єкту результатом функції. Найчастіше ця ситуація виникає

при довизначенні операцій. Вона буде розглянута в наступному розділі. Ось ще

один типовий приклад. Розглянемо таку послідовність дій

 

Screen myScreen(3,3,””);

myScreen.clear();

myScreen.move(1,1);

myScreen.set(’*’);

myScreen.move(2,2);

myScreen.set(’*’);

myScreen.move(3,3);

myScreen.set(’*’);

myScreen.show();

 

Існує простий спосіб, як записати цю послідовність інструкцій у вигляді одного

виразу

 

myScreen.clear().move(1,1).set(’*’).

move(2,2).set(’*’).

move(3,3).set(’*’).show();

 

Для цього функції повинні повертати значенням свій поточний об'єкт

 

Screen& Screen::home()

{

_cursor=0;

return *this;

};

Screen& Screen::move(int i, int j)

{

if ((i>=_height) || (j>=_width))

_cursor=0;

else

_cursor = _width*i + j;

return *this;

};

Screen& Screen::move()

{

if ((++_cursor)>=_width*_height) _cursor=0;

return *this;

};

Screen& Screen::set(char a)

{

*(_wContent + _cursor)=a;

return *this;

}

Screen& Screen::clear()

{

for (int i=0; i<_height*_width; i++)

*(_wContent+i)= _filler;

_cursor=0;

return *this;

}

 

та відповідно модифікувавши інтерфейс

class Screen

{

public:

Screen(int, int, char*);

Screen(const Screen&);

~Screen();

 

void home() const;

void move(int, int) const;

void move() const;

char get() const;

void show() const;

void copy(const Screen&);

 

Screen& home();

Screen& move(int, int);

Screen& move();

Screen& set(char);

Screen& clear();

 

private:

static const int maxHeight;

static const int maxWidth;

int _height;

int _width;

char *_wContent;

mutable int _cursor;

char _filler;

};

 

 

Зверніть увагу на те, що ново визначені функції не константні. В принципі

їх можна визначати константними, якщо добавити кваліфікатор const двічі

 

const Screen& home() const;

 

інакше значення об'єкту можна було б змінити, помістивши виклик в позицію,

яка вимагає лівостороннього значення.

Тепер розглянемо проблеми непрямого доступу до членів класу за допомогою указників

у об'єктах.

 

Ось простий клас калькулятора з суматором і двома регістрами

 

class Calc

{

friend void utilityCalc();

unsigned int a;

unsigned int x;

unsigned int y;

public:

unsigned int add();

unsigned int mult();

unsigned int div();

unsigned int subt();

}

 

Арифметичні операції виконуються на суматорі, наприклад,

 

unsigned int Calc::add() { return a=a+x;}

 

Якщо ми візьмемо об'єкт c класу Calc, то атрибут c.a матиме тип unsigned

int. Це значить, що будь-якій змінній i типу unsigned int можна присвоїти

значення c.a. Спроба присвоїти змінній i просто значення a ясна річ не коректна.

Так само як некоректна спроба помістити на a указник типу unsigned int*. Дійсно

для прямого чи непрямого звертання до атрибуту необхідно знати об'єкт.

 

void utilityCalc()

{

Calc c;

unsigned int i, *ip;

i=c.a; //OK

i=a; //Error

ip=&a; //Error

}

 

Але все ж таки можна домовитися позначати певний атрибут в об'єкті за допомогою

указника, надавши указнику іншого типу, наприклад, anyReg

 

unsigned int Calc::*anyReg;

 

визначено як указник атрибуту типу unsigned int в об'єкті типу Calc.

 

Відповідно надамо йому значення

 

anyReg = &Calc::a;

 

сказавши, що указник показуватиме на атрибут у об'єктах Calc.

 

Також можна скористатися визначенням нового типу

 

typedef unsigned int Calc::*Register;

 

Register r = &Calc::a;

 

Щоб добратися до самого атрибуту об'єкт все ж таки потрібен

 

Calc c;

 

c.*anyReg = 1;

 

присвоєння відбудеться атрибуту, на який вказує anyReg, тобто c.a.

 

Ось трохи складніший приклад

 

void CycleShiftLeft

(unsigned int Calc::*regPtr, Calc c[], int n)

{

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

{

unsigned int mask = 0x80000000;

mask = (c[i].*regPtr & mask) << 31;

(c[i].*regPtr<<=1)|=mask;

}

}

 

В масиві c відбувається циклічний зсув атрибуту, позначеного указником regPtr

.

 

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

Те що об'єкти класу розділяють функції не спрощує проблеми, кожна функція знає

свій об'єкт, який її активізував. Для цього в ній присутній указник this.

Ось, наприклад, указник op на функцію без параметра з класу Calc

 

unsigned int (Calc::*op) ();

 

Доповнимо клас Calc функцією exe, що виконує довільну операцію калькулятора

 

class Calc

{

friend void utilityCalc();

unsigned int a;

unsigned int x;

unsigned int y;

public:

unsigned int add();

unsigned int mult();

unsigned int div();

unsigned int subt();

unsigned int exe(unsigned int (Calc::*) ());

}

 

її реалізація

 

unsigned int Calc::exe(unsigned int (Calc::*op) ())

{

return (this->*op)();

}

 

 

А ось приклад виклику

 

void utilityCalc()

{

Calc b;

b.x=10; b.a=1;

cout<<b.exe(&Calc::add)<<endl;

}

 

Ще один приклад показує особливості посилань на константні і неконстантні

функції

 

typedef void (Screen::*Action) () const;

void doAction1(Screen& s, Action act, int n)

{

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

(s.*act)();

}

 

typedef Screen& (Screen::*AnotherAction) ();

void doAction2(Screen& s, AnotherAction act, int n)

{

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

(s.*act)();

}

 

Тут визначено дві позакласні функції для повторення довільної дії з класу

Screen. А ось приклад їх виклику

 

Screen v(2,4,"11223344");

doAction1(v, Screen::move, 7);

doAction1(v, Screen::back, 5);

doAction2(v, Screen::move, 7);

doAction2(v, Screen::back, 2);

 

Пересвідчіться, що doAction1 викликає функцію з сигнатурою

 

void move() const;

 

а doAction1

 

Screen& move();

 

Також можна влаштувати масив з указників на класні функції

 

Action menu[] =

{

Screen::home,

Screen::move,

Screen::back,

Screen::show

};

 

Указники на статичні атрибути і статичні методи нічим не відрізняються від

звичайних указників даних і функцій, оскільки статичні атрибути знаходяться

поза об'єктами, а статичні функції не знають поточного об'єкту.

 

class Account

{

friend class Bank;

static double interestRate;

public:

static void raiseInterest(double);

static double getInterest();

};

 

class Bank

{

double * RatePtr;

void (* raisePtr)(double);

double (* getPtr)();

public:

Bank();

};

 

Bank::Bank()

{

RatePtr = &Account::interestRate;

raisePtr = &Account::raiseInterest;

getPtr = &Account::getInterest;

}

 

 




Поделиться с друзьями:


Дата добавления: 2014-01-06; Просмотров: 355; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.05 сек.