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

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

Свойства дружественных функций:

· объявляются внутри класса, к элементам которого им необходим доступ. При этом используется служебное слово friend. В качестве параметра дружественной функции передается объект или ссылка на объект в классе, т.к. this ей не передается (поскольку она не является членом класса).

· она может быть обычной функцией, либо методом другого, ранее определенного класса. На дружественные функции не распространяется действие спецификаторов доступа. Место ее размещения и объявления в классе безразлично.

· функция, объявленная дружественной, может являться таковой сразу в нескольких классах.

Пример 1

class monstr; /* предварительное объявление класса */

Class hero

{

public:

void kill(monstr &);

};

Class monstr

{

friend int steal_ammo(monstr &);

friend void hero::kill(monstr &);

/* класс hero должен быть определен ранее */

};

int steal_ammo(monstr &M)

{

return --M.ammo;

}

void hero::kill(monstr&M)

{

M.health = 0;

M.ammo = 0;

}

 

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


 


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

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

Пример 1

Class hero

{

friend class mistress;

};

Class mistress

{

void f1(...);

void f2(...);

};

 

Класс сам определяет, какие функции являются дружественными, а какие нет. Функции f1 и f2 – дружественные по отношению к классу hero и имеют прямой доступ ко всем полям класса.

Пример 2

#include <stream.h>

struct employee {

friend class manager;

employee* next;

char* name;

short department;

virtual void print();

};

struct manager: employee {

employee* group;

short level;

void print();

};

Void employee::print()

{

 cout << name << "\t" << department << "\n";

}

Void manager::print()

{

employee::print();

cout << "\tlevel " << level << "\n";

}

void f(employee* ll)

{

 for (; ll; ll=ll->next) ll->print();

}

Main ()

{

employee e;

e.name = "J. Brown";

e.department = 1234;

e.next = 0;

manager m;

m.name = "J. Smith";

m.department = 1234;

m.level = 2;

m.next = &e;

f(&m);

}


 


Глава 5. Локальные и вложенные классы

Локальный класс

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

 

Локальный класс не может иметь статических полей. Объекты локального класса могут быть созданы только внутри функции в области действия объявления класса, причем все компонентные функции локального класса должны быть встраиваемыми:

Пример 1

int x;

Void f()

{

 static int s;

 int x;

 extern int g();

 struct local {

   int g() { return x; } // ошибка, auto x

   int h() { return s; } // OK

   int k() { return::x; } // OK

   int l() { return g(); } // OK

 };

 //...

}

local* p = 0; // ошибка: нет local в текущем контексте



Вложенный класс.

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

Пример 1

class Figura {

Class Point          // вложенный класс Point

{

int x, y, color;

public:

int getx() { return x; }

int gety() { return y; }

int getcolor() { return color; }

.......

};

Class Line           // вложенный класс Line

{

Point Tn, Tk;        // начало и конец линии

public:

Void draw(void)      // метод рисования линии

{

....

line(Tn.getx(), Tn.gety(), Tk.getx(), Tk.gety());

}

void setline(int ncolor) {....}

};

};

 

 

Пример 2

int x;

   int y;

   class enclose {

   public:

    int x;

    static int s;

    class inner {

       void f(int i)

       {

         x = i; // ошибка: присваивание enclose::x

         s = i; // нормально: присваивание enclose::s

        ::x = i; // нормально: присваивание глобальному x

         y = i; // нормально: присваивание глобальному y

       }

       void g(enclose* p, int i)

       {

         p->x = i; // нормально: присваивание enclose::x

       }

     };

   };

   inner* p = 0; // ошибка: `inner' вне области видимости



Глава 6. Наследование

Наследование

Наследование позволяет строить иерархии, в которых производные классы получают элементы родительского (родительских) или базового классов и могут дополнять или изменять его свойства. Классы, находящиеся ближе к началу иерархии, объединяют в себе наиболее общие черты для всех нижележащих классов. По мере продвижения вниз иерархии, классы приобретают все более индивидуальные черты. Множественное наследование позволяет одному классу обладать свойствами многих родительских. Причем любой класс может быть объявлен потомком ранее описанного класса.

Доступ к полям, описанным в классе родителя, осуществляется так же, как в собственном. Поиск метода в иерархии класса выполняется следующим образом:

· в первую очередь компилятор устанавливает тип объекта

· ищет метод в классе объекта, если находит, то подключает его

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

· если метод не найден, выдается ошибка

Механизм наследования обеспечивает создание дерева родственных классов. Дерево может иметь несколько уровней, причем на каждом из них могут добавляться новые поля и методы.

Ключи доступа при наследовании

При описании класса в его заголовке перечисляются все классы, являющиеся для него базовыми. Возможность обращения к элементам этих классов регулируется с помощью ключей доступа: private, protected, public.

Пример 1

class name:[private|protected|public] parent_class;

class A{...};

class B{...};

class C{...};

class D:A, protected B, public C {...};


Ключ доступа Спецификатор в базовом классе Спецификатор в производном классе
private private no
  protected private
  public private
protected private no
  protected protected
  public protected
public private no
  protected protected
  public public

 

Private элементы базового класса в производном классе недоступны в производном классе независимо от ключа доступа. Обращение к ним может осуществляться только через элементы базового класса.

Protected элементы с ключом private становятся private, в остальных случаях права доступа к ним не изменяются. Если базовый класс наследуется с ключом private, можно выборочно сделать некоторые элементы доступными в производном классе. Для этого их необходимо объявить в секции public производного класса с помощью оператора доступа к области видимости (::).

Пример 2


Class base

{

...

public: void f();

};


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: