<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Два студента пишут лабы &#187; С++</title>
	<atom:link href="http://www.studcode.ru/archiv/tag/cpp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.studcode.ru</link>
	<description>Конспекты лекций, самостоятельные работы по  delphi, с++, php. Курсовые проекты. Все что мы сделали вы можете скачать без проблем!</description>
	<lastBuildDate>Wed, 27 Oct 2010 15:15:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Наследование</title>
		<link>http://www.studcode.ru/archiv/nasledovanie/</link>
		<comments>http://www.studcode.ru/archiv/nasledovanie/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 20:10:48 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=387</guid>
		<description><![CDATA[Наследование — механизм создания нового класса из старого. То есть, к существующему классу можно что-либо добавлять, или изменять его каким-то образом для создания нового (порожденного) класса. Это мощный механизм для повторного использования кода. Наследование позволяет создавать иерархию связанных типов, совместно использующих код и интерфейс.
Большинство полезных типов представляют собой различные варианты друг друга, поэтому утомительно выписывать [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Наследование</strong> — механизм создания нового класса из старого. То есть, к существующему классу можно что-либо добавлять, или изменять его каким-то образом для создания нового (порожденного) класса. Это мощный механизм для повторного использования кода. Наследование позволяет создавать иерархию связанных типов, совместно использующих код и интерфейс.<br />
Большинство полезных типов представляют собой различные варианты друг друга, поэтому утомительно выписывать для каждого один и тот же код. Порожденный класс наследует описание основного класса. Затем он может изменяться с помощью добавления членов, изменения функций существующих членов и изменения привилегий доступа. Удобство этого понятия можно показать на примере таксономической классификации, компактно подводящей итог больших областей знания. Например, если известно понятие &#8220;млекопитающее&#8221;, а так же, что слон и мышь являются млекопитающими, то их описания можно сделать значительно более сжатыми, чем в другом случае. Корневое понятие &#8220;млекопитающее&#8221; содержит информацию о том, что млекопитающие — это теплокровные животные, относящиеся к высшим позвоночным, которые выкармливают своих детенышей молоком, производящимся в их молочных железах. Эта информация унаследована и мышью, и слоном, но она выражается только один раз: в корневом понятии &#8220;млекопитающее&#8221;. В C++ это означает, что признаки обоих классов, слона и мыши, порождены от основного класса, &#8220;млекопитающее&#8221;.<br />
<span id="more-387"></span><br />
В терминах ООП, оператор &#8220;elephant ISA mammal&#8217; описывает отношение. Если есть цирк, в котором есть несколько слонов, то в этом случае объект цирк может иметь члены типа слон. В таком случае, выражение &#8220;class circus HASA elephant&#8217; описывает отношение подразделения.<br />
C++ поддерживает virtual функции-члены, которые объявлены в основном классе и переопределены в порожденном классе. Иерархия классов, определенная общим наследованием, создает связанный набор типов пользователя, на которые можно ссылаться с помощью указателя базового класса. При обращении к виртуальной функции через этот указатель в C++ выбирается соответствующее функциональное определение во время выполнения. Объект, на который указывается, должен содержать в себе информацию о типе, поскольку различие между ними может быть сделано динамически. Эта особенность типична для ООП кода. Каждый объект &#8220;знает&#8221;, как на него должны воздействовать. Эта форма полиморфизма называется чистым полиморфизмом.<br />
Способность к наследованию должна быть встроена в программное обеспечение для того, чтобы максимизировать многократное использование кода и позволить естественное моделирование предметной области. С использованием наследования ключевыми элементами методологии ООП становятся:<br />
• разработка соответствующего набора типов;<br />
• проектирование их возможных связей и применение механизма наследования для совместного использования кода;<br />
• использование виртуальных функций для полиморфической обработки связанных объектов.<br />
<strong><span style="text-decoration: underline;">ПОРОЖДЕННЫЕ КЛАССЫ</span></strong><br />
Для порождения нового класса от существующего может использоваться следующая форма записи:<br />
class ИмяКласса:(public|protected|private) ИмяБазовогоКласса<br />
{<br />
объявления членов<br />
} ;<br />
Как обычно, ключевое слово class может быть заменено ключевым словом struct, с той разницей, что по умолчанию члены будут public. Одна из особенностей порожденного класса &#8211; видимость унаследованных членов. Для определения доступности членов основного класса порожденному классу используются ключевые слова public, protected и private. Пример порождения класса:</p>
<pre><code>
enum year ( fresh, sops,	junior,	senior,	grad};
class student { protected:
int    student id;
double gpa;
year   у ;
char   name[30];
public:
student(char* nm, int id, double g, year x);
void print();
};
enum support { ta, ra, fellowship, others-class grad_student: public student { protected:
support s;
char    dept[10] ;
char    thesis [80];
public:
grad student (char* nm, int id, double g, year x, support t, char* d, char* th);
void print() ;
};</code></pre>
<p>В этом примере grad_student &#8211; порожденный класс, a student -базовый класс.  Общее наследование также означает, что полученный класс grad_student &#8211; подтип класса student. Следовательно, студент, окончивший институт &#8211; студент, но студент не может быть студентом, окончившим институт.<br />
Порожденный класс представляет собой модификацию основного класса, которая наследует общие и защищенные члены базового класса. Таким образом, в примере grad_student члены student &#8211; student_id,gpa, name, year и print унаследованы. Часто порожденный класс добавляет новые члены к уже существующим членам класса, как в случае grad student, который имеет три новых члена-данных и переопределенную функцию-член print. Функция-член print перекрывается. Это означает то, что порожденный класс имеет реализацию функции-члена, отличающуюся от базового.<br />
//Проверка правил преобразования указателей.</p>
<pre><code>
main (){
student s("Mae Pohl", 100, 3.425, fresh), *ps = &amp;s ;
grad student gs("Morris Pohl", 200, 3.2564, grad, ta,
"Pharmacy", "Retail Pharmacies"), *pgs;
ps -&gt; print(); //student::print
ps = pgs = &amp;gs;
ps -&gt; print(); //student::print
pgs -&gt; print(); //grad_student::print }</code></pre>
<p><strong>Видимость в производном классе</strong><br />
Ограничение доступа private, protected или public для базового класса имеют следующий смысл для зоны видимости членов базового класса в производном. По умолчанию устанавливается private.<br />
•	Скрытые члены базового класса недоступны в производном.<br />
•	При объявлении Private &#8211; открытые и защищенные члены базового класса в производном классе становятся закрытыми.<br />
•	При объявлении Protected &#8211; открытые и защищенные члены базового класса в производном классе становятся защищенными .<br />
•	При объявлении Public &#8211; открытые и защищенные члены базового класса в производном классе становятся открытыми и защищенными соответственно.<br />
Графически эти правила представлены на рисунке :<br />
Объявление 	Базовый класс 		Производный класс<br />
Private<br />
private		private<br />
protected		protected<br />
public		public</p>
<p>Protected	private		private</p>
<p>protected		protected<br />
public		public</p>
<p>Public<br />
private		private</p>
<p>protected		protected</p>
<p>public		public<br />
<strong><span style="text-decoration: underline;">ПОВТОРНОЕ ИСПОЛЬЗОВАНИЕ КОДА:<br />
ГРАНИЦЫ ДИНАМИЧЕСКОГО МАССИВА</span></strong><br />
Приведем пример класса безопасного массива vect. Используем код повторно и расширим этот тип до безопасного массива с динамическими пределами &#8211; верхним и нижним. Такой стиль объявления массива более гибок и позволяет индексам непосредственно соответствовать прикладной области. Вспомните, что тип динамического безопасного массива vect проверяет индексы на нахождение в диапазоне пределов массива и создает массивы с использованием свободной памяти. Объявление класса следующее:<br />
//Реализация типа динамического безопасного массива vect</p>
<pre><code>
class vect
{private:
int *p;                 //базовый указатель int
size;                   //число элементов
public:                  //конструкторы и деструкторы
vect();                 //создает массив размерностью 10
vect(int 1);            //создает массив размерностью 1
vect(vect&amp; v);           //инициализация от vect
vect(int  a[], int 1);  //инициализация от массива
~vect() { delete [] р; }
int ub() { return (size - 1); } //верхняя граница ints
operator[](int i) ;        //получение элемента
//проверенного на соответствие границам
vect&amp; operators(vect&amp; v);
vect operator+(vect&amp; v);
};</code></pre>
<p>Производный тип будет иметь private члены 1_bnd и u_bnd, которые будут хранить нижний и верхний пределы созданного безопасного массива. Производный тип повторно использует представление и код исходного типа.<br />
//Порожденный сип безопасного массива — класс vect_bnd class vect_bnd: public vect</p>
<pre><code>
{private:
int l_bnd, u_bnd;
public:
vect_bnd() ;
vect bnd(int, int);
int&amp; operator[](int) ;
int ub() { return (u_bnd); }
int lb() { return (l_bnd); }
};</code></pre>
<p>Конструкторы порожденного класса вызывают конструкторы базового класса. При этом используется такая же синтаксическая конструкция, как и для инициализации членов.<br />
заголовок функции: имя_базового_класса(список_параметров)<br />
В более ранних компиляторах C++ в случае одиночного наследования имя базового класса могло быть опущено и понималось неявно. Сейчас это считается анахронизмом.</p>
<pre><code>
vect bnd::vect bnd() :vect(10)
{l_bnd = 0;
u_bnd = 9;
}
vect bnd::vect bnd(int lb, int ub) :vect(ub - lb + 1)
{l_bnd = lb;
u_bnd = ub;
}</code></pre>
<p>Обратите внимание на то, как конструкторы порожденного класса вызывают конструкторы базового класса. Дополнительный код инициализирует пару<br />
границ диапазона. В качестве альтернативы это может быть выполнено инициализаций в списке.<br />
vect_bnd::vect_bnd(int lb, int ub):<br />
vect(ub &#8211; lb + 1), l_bnd(lb), u_bnd(ub) {}</p>
<p>Можно также повторно использовать код при перегрузке оператора индексации [].<br />
Int&amp; vect_bnd::operator[](int i)<br />
{if (i &lt; l_bnd || u_bnd &lt; i)<br />
{ cerr « &#8220;index out of range\n&#8221;;<br />
exit(1) ;<br />
};<br />
return (vect::operator[](i &#8211; l_bnd));<br />
}<br />
Это будет очень неэффективно. Почему? Потому, что проверка пределов теперь выполняется дважды. Чтобы избежать этого, необходимо внести два изменения. Во-первых, нужно изменить привилегию доступа члена vect: : р на protected, так что бы порожденный класс имел непосредственный доступ к ранее private реализации vect. Это позволяет сделать второе изменение:<br />
использовать р в функции-члене vect_bnd: : operator[](). Теперь код будет более эффективным:</p>
<pre><code>
int&amp; vect_bnd::operator [](int i)
{if (i &lt; l_bnd || u_bnd &lt; i)
{cerr « "index out of range\n";
exit(1) ;
};
return (p[i - l_bnd]) ;
}</code></pre>
<p>Обратите внимание на компромисс между повторным использованием кода и эффективностью. Это распространенный случай. Отметьте также, что наследование требует учета трех уровней доступа. Что должно быть строго частным (private), а что будет защищено (protected), зависит от того, что потенциально может быть повторно использовано.</p>
<p><span style="text-decoration: underline;"><strong>Совместимость типов предка и потомка.</strong></span><br />
•	Класс потомок всегда можно преобразовать в класс предок.<br />
•	Всегда и во всех конструкциях класс потомок может использоваться через ссылку на класс предок.<br />
•	Ссылка на класс предок может указывать любой из классов потомков без явного преобразования.<br />
•	Указатель на класс потомок может содержать класс предок, но требуется явное преобразование типов. Это очень опасная практика.</p>
<pre><code>
Class A{  …};
Class B:public A{…};

A A1;*A2;
B B1,*B2;

A=B;          //Допустимо
B=A;          //Не допустимо
B2=B(&amp;A);     //Допустимо но может привести к ошибке
A2=B;         //Допустимо
</code></pre>
<p><strong><span style="text-decoration: underline;">Виртуальные методы. Полиморфизм.</span></strong><br />
Виртуальные методы объявляются с использованием служебного слова virtual. Методы объявленные один раз виртуальными, будут виртуальными во всех потомках. В потомках они могут объявляются как с использованием  слова virtual так и без него, в  любом случае они остаются виртуальными.</p>
<p>•	Позволяют выбирать функции члены с одним и тем же именем через указатель функции в зависимости от типа созданного объекта, а не от типа указателя.<br />
•	Является одним из типов полиморфизма.<br />
•	Типы аргументов, их количество, а также тип возвращаемого значения должны быть у перегруженных функций одинаковыми.<br />
•	Не могут быть статическими.</p>
<p>Рассмотрим пример использования виртуальных классов.</p>
<pre><code>
#include
#include
#include
#include
enum Boolean{False,True};
const
int DefColor=1, BackColor=0;

class TShape
{protected:
int x,y;
int color;
Boolean Visible;
virtual void Paint(int Color)=0;
public:
TShape(int X,int Y,int Color):x(X),y(Y),color(Color){};
void Draw(){Paint(color); Visible=True; };
void Clear(){Paint(BackColor);Visible=False; }
void Move(int X,int Y);
};

void TShape::Move(int X,int Y)
{
if (Visible) Clear();
x=X;
y=Y;
Draw();
}

class TPixel: public TShape
{protected:
void Paint(int Color);
public:
TPixel(int X,int Y,int Color):TShape(X,Y,Color){};
};

void TPixel::Paint(int Color)
{
putpixel(x,y,Color);
}

class TCircle:public TShape
{protected:
int radius;
void Paint(int Color);
public:
TCircle(int X,int Y,int Radius,int Color)
:TShape(X,Y,Color), radius(Radius){};
};

void TCircle::Paint(int Color)
{
setcolor(Color);
circle(x,y,radius);
}

void main()
{TShape Shape(10,10,3);  // ОШИБКА нельзя создать экземпляр
// виртуального класса
TShape *PShape;
TPixel Pixel(10,20,15);
TCircle Circle(150,150,50,3);

int gdriver = DETECT, gmode;
initgraph(&amp;gdriver, &amp;gmode, "..\\bgi\\");
if (graphresult() != grOk)  /* an error occurred */
{
cout&lt;&lt;"Graphics error"&lt;
cout&lt;&lt;"Press any key to halt";       getch();       exit(1);             /* return with error code */    }  Pixel.Draw();  Circle.Draw();  PShape=&amp;Pixel;  PShape-&gt;Clear();
Circle.Move(400,150);

getch();
closegraph();
}
</code></pre>
<p>Виртуальные методы могут быть так же и перегружаемыми, но этот подход может привести к неожиданным последствиям.</p>
<pre><code>
#include
class A
{public:
int f;
virtual void fun(int a){cout&lt;&lt;"A::fun(int)"&lt;<a>&lt;
virtual void fun(double a){cout&lt;&lt;"A::fun(double)"&lt;</a><a>&lt;
};
class B:public A
{public:
int c;
virtual void fun(int a){cout&lt;&lt;"B::fun(int)"&lt;</a><a>&lt;fun(4.5);      //A::fun(double)
pa-&gt;fun(4);        //B::fun(int)
pb=&amp;b;
pb-&gt;fun(4.5);      //B::fun(int)
pb-&gt;fun(4);        //B::fun(int)
return 0;
}</code></pre>
<p><span style="text-decoration: underline;"><strong>Абстрактные классы</strong></span><br />
Базовый класс иерархии типа обычно содержит ряд виртуальных функций, которые обеспечивают динамическую типизацию. Часто в базовом классе эти виртуальные функции фиктивны и имеют пустое тело. Определенное значение им придают в порожденных классах. В C++ для этой цели применяется чистая виртуальная функция. Чистая виртуальная функция — виртуальная функция-член, тело которой обычно не определено. Запись этого объявления внутри класса следующая:<br />
virtual прототип_функции = 0;<br />
Чистая виртуальная функция используется для того, чтобы &#8220;отложить&#8221; решение о реализации функции. В ООП терминологии это называется отсроченным методом.<br />
Класс, имеющий, по крайней мере, одну чистую виртуальную функцию — абстрактный класс. Для иерархии типа полезно иметь базовым абстрактный класс. Он содержит общие базовые свойства порожденных классов, но сам по себе не может использоваться для объявления объектов. Напротив, он используется для объявления указателей, которые могут обращаться к объектам подтипа, порожденным от абстрактного класса.<br />
<strong><span style="text-decoration: underline;">Множественные базовые классы&#8230;</span></strong><br />
•	Позволяют порождать класс от более чем одного класса . Порядок представления базовых классов в списке не существенен .<br />
•	К членам базовых классов, имеющим одинаковые имена, доступ должен осуществляться через имена базовых классов, которым они принадлежат, при помощи операции разрешения доступа.</p>
<pre><code>
class А { public: void f(); /* ... */ };
class В { public: void f(); /* ... */ };
class С : public A, public В { /* ... */ };</a>

<a>С с;
c.f(); // Ошибка: какое f(), из А или из В?
c.A::f(): // Правильно
</code></pre>
<p>Наиболее удобно разрешать такую неоднозначность перекрытием в производном классе обеих функций.</p>
<pre><code>
class С : public A, public В
{public:
void f() {A::f(); B::f();} // Перекрываем обе функции II...
}
c.f(): // Теперь правильно
</code></pre>
<p>•	Для классов, порожденных от производных классов с общей базой, по умолчанию существует два экземпляра объекта общей базы.</p>
<pre><code>
Struct X{intl; /*...*/X(int);};
class A public X { /* ... */ A( int); };
class В public X { /* ... */ B( int): };
class С public A, public В {/*...*/ C( int);};</code></pre>
<p>Графически это может быть представлено следующим образом:<br />
К членам общего базового класса можно обратиться через имя одного из производных классов, используя оператор разрешения доступа.</a></p>
<pre><code>
<a>С с( 0 );
++c.i; // Ошибка: какое I, из А или из В? ++c.A::i; // Правильно
С * ср = new C( 0 );
Х * хр = ср; // Ошибка: какое X?
Х*хр=(А*)ср; //Правильно
</code></pre>
<p><strong><span style="text-decoration: underline;">Виртуальные базовые классы&#8230;</span></strong><br />
•	Для классов, порожденных от производных классов с общим виртуальным базовым классом, существует только один экземпляр объекта общего базового класса.<br />
•	Объявляются включением ключевого слова virtual в спецификатор ограничения доступа базового класса .</a></p>
<pre><code>
<a>struct V {int i; /*...*/V(int);};
class A virtual public V { /* ... */ A( int); };
class В virtual public V { /* ... */ B( int); };
class С public A, public В { /* ... */ C( Int); };</a></code></pre>
<p><a>Графически это представлено на рисунке.</a></p>
<p><a>Для доступа к членам общего базового класса не требуется ничего указывать дополнительно, поскольку существует лишь один объект этого класса.<br />
Если виртуальный базовый класс и производный класс разделяют имя какого-либо поля, фукции-члена или перечисления, то имя в производном классе скрывает имя в виртуальном базовом классе.<br />
<code>
<pre>
Struct V {int i; void f(); /* ... */ };
struct A :virtual V{int i; int f(); /* ... */ };
struct В :virtual V{ /* ... */};
class С : public A, public В
{
...
void g(){i=f();} //Правильно
// Как A::i, так и A::f() скрывают V::i и V::f()
};</code></pre>
<p>Их конструкторы, если таковые имеются, вызываются конструктором последнего класса в цепочке производных классов.<br />
•	В объявлении класса могут быть смешаны с невиртуальными базовыми классами.<br />
•	Могут вызывать нежелательные множественные вызовы методов в витруальном базовом классе, если переопределенный метод вызывает метод-предок в обоих классах предках, то метод в базовом классе вызывается 2 раза</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/nasledovanie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Перегружаемые оператор присваивания  и индексированные операторы</title>
		<link>http://www.studcode.ru/archiv/peregruzhaemye-operator/</link>
		<comments>http://www.studcode.ru/archiv/peregruzhaemye-operator/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 19:44:01 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=384</guid>
		<description><![CDATA[В C++ имеются объявления ссылок. Модификаторы такого типа создают именующие выражения (адреса переменной). Помните, lvalue обозначает значение месторасположения. Справа от выражения назначения именующее выражение (адрес переменной) автоматически разыменовывается. Слева от выражения назначения именующее выражение (адрес переменной) определяет, где должно храниться соответствующее значение. Как индексация, так и назначение используют эти свойства именующих выражений (адресов переменной). Для [...]]]></description>
			<content:encoded><![CDATA[<p>В C++ имеются объявления ссылок. Модификаторы такого типа создают именующие выражения (адреса переменной). Помните, lvalue обозначает значение месторасположения. Справа от выражения назначения именующее выражение (адрес переменной) автоматически разыменовывается. Слева от выражения назначения именующее выражение (адрес переменной) определяет, где должно храниться соответствующее значение. Как индексация, так и назначение используют эти свойства именующих выражений (адресов переменной). Для АТД необходимо определять такие выражения в том случае, если подходящие значения по умолчанию недоступны. Перепишем класс vect из раздела 6.5, расширив его функциональность применением перегрузки операторов.<br />
<span id="more-384"></span><br />
Переделанный класс обладает несколькими усовершенствованиями, которые делают его более надежным и полезным. Добавляется конструктор, который преобразует обычный массив целых чисел в безопасный. Это делается для того, чтобы позволить нам разрабатывать код, использующий безопасные массивы, а затем преобразовывать тот же код для эффективного использования обычных массивов. Public член-данные ub заменяется на функцию-член. Это предотвращает пользователя от ошибки в программе при неосторожной модификации члена. В заключение, перегружается оператор индексации [ ], заменяя функцию-член element.</p>
<pre><code>
//Тип безопасного массива vect с	перегрузкой []
class vect
{private:
int* p;    //базовый указатель
int  size; //число элементов
public:
//конструкторы и деструкторы
vect();      //создает массив из 10	элементов
vect(int п); //создает массив из п элементов
vect(const vect&amp; v);       //инициализируется vect
vect(const int a[], int n);//инициализируется массивом
~vect() { delete [] p} ;
//прочие функции-члены
int ub(){return (size -1);} //проверка верхней границы
int&amp; operator[](int i);     //элемент, проверяемый на
//выход за пределы
vect&amp; operator=(const vect&amp; v); //перегруженное присвоение
vect operator+(const vect&amp; v);  //перегруженное сложение
};</code></pre>
<p>Добавляются два вспомогательных конструктора:<br />
vect::vect(const int а[],int n) //преобразует обычный массив</p>
<pre><code>
{if (n &lt;= 0)
{
cerr&lt;&lt;"illegal vect size: "&lt;&lt;
exit(l) ;
}
size = n;
p = new int[size]; for (int i = 0; i &lt; size; ++i)
p[i] = a[i];
}

vect::vect(const vect&amp; v) //конструктор копии
{size = v.size;
p = new int[size];
for (int i = 0; i &lt; size; ++i) p[i] = v.p[i] ;
}
</code></pre>
<p>Перегруженный оператор индексации берет целый аргумент и проверяет, находится ли это значение в пределах диапазона. Если да, он использует это значение для того, чтобы возвратить адрес индексированного элемента.</p>
<pre><code>
intS vect::operator [](int i)
{if (i &lt; 0 || i &gt; (size - 1)) {
cerr&lt;&lt;"illegal vect index: "&lt;<em>&lt;
exit(l) ;
}
return (p[i]);
}</em>
</code></pre>
<p><em>Перегруженный оператор индексации имеет возвращаемый тип и один параметр. Он должен быть нестатической функцией-членом. Хорошим стилем будет поддержание непротиворечивости между значением оператора индексации [ ], который определяется пользователем, и стандартным его значением. Таким образом, наиболее общий прототип функции:<br />
имякласса&amp; operator[] (целостный тип);</em></p>
<p><em>В таких функциях возвращается значение ссылки, которое может использоваться с любой стороны выражения назначения.<br />
Когда назначение не перегружено, оно определяется по умолчанию с семантикой, представляющей собой присвоение значения. Это иногда называется семантикой &#8220;поверхностного копирования&#8221; и не всегда допустимо (см. главу 6). Удостовериться в том, что семантика по умолчанию правильна входит в обязанности поставщика класса. Если нет, как это произошло в нашем случае с vect, поставщик класса должен перегрузить конструкцию с правильной семантикой. Возможно также перегрузить оператор назначения с обработкой сигналов ошибочного поведения.<br />
В следующем примере функция-член перегружает назначение для класса vect:</em></p>
<pre><code>
<em>vectS vect::operator=(const vectS v)
{int s = (size &lt; v.size) ? size : v.size;
if (v.size != size)
cerr&lt;&lt;"copying different size arrays "
&lt;&lt;&lt;" and "&lt;&lt;
for (int i = 0; i &lt; s;	++i) p[i] = v.p[i] ;
return (*this) ;
}</em></code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/peregruzhaemye-operator/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Перегружаемые операторы</title>
		<link>http://www.studcode.ru/archiv/peregruzhaemye-operatory/</link>
		<comments>http://www.studcode.ru/archiv/peregruzhaemye-operatory/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 19:39:35 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=382</guid>
		<description><![CDATA[Для перегрузки встроенных операторов С ++ можно использовать ключевое слово operator. Как и имени функции &#8211; типа print &#8211; ему может быть присвоен ряд значений, зависящих от параметров. Таким образом, опе-ратору типа + также можно присваивать дополнительные значения. Перегрузка операторов позволяет АТД использовать синтаксис выражений C++. Это облегчает написание программ и делает их более читабельными. [...]]]></description>
			<content:encoded><![CDATA[<p>Для перегрузки встроенных операторов С ++ можно использовать ключевое слово operator. Как и имени функции &#8211; типа print &#8211; ему может быть присвоен ряд значений, зависящих от параметров. Таким образом, опе-ратору типа + также можно присваивать дополнительные значения. Перегрузка операторов позволяет АТД использовать синтаксис выражений C++. Это облегчает написание программ и делает их более читабельными. Функция тру из предыдущего раздела могла быть написана как<br />
vect operator* (const vect&#038; v, const matrix&#038; m)<br />
где * является двухместным оператором умножения. После этого, если г и s были vect, a t была matrix, функцию умножения будет вызывать естественное выражение<br />
r = s * t;<br />
Это заменит запись функции    r = mpy(s,t);<br />
Перегруженный оператор можно вызывать и с использованием функциональной формы записи:<br />
r = operator*(s,t);<br />
Хотя операторам и могут добавляться новые значения, их ассоциативность и приоритет остаются прежними. Например, оператор умножения сохраняет более высокий приоритет, чем оператор сложения. (Таблица приоритетов операторов для C++ включена в приложение В.) Перегружены могут быть почти все операторы. Имеются следующие исключения: оператор членства &#8220;.&#8221;, оператор селектора члена объекта &#8220;. *&#8221; (см. главу 8), троичный условный оператор выражения &#8220;? : &#8220;, оператор &#8220;sizeof&#8221; и оператор разрешения контекста<br />
Доступны все арифметические, логические операторы, операторы сравнения, равенство, присвоение, операторы поразрядных операций, префиксные и постфиксные формы операторов приращения и декремента. Могут быть перегружены оператор индексации &#8220;[ ]&#8221; и обращение к функции &#8220;()&#8221;. Также могут быть перегружены оператор указателя класса &#8220;->&#8221; и оператор указателя на член &#8220;->*&#8221; (см. главу 8). Возможна перегрузка new и- delete.<br />
Операторы иногда нуждаются также в доступе к скрытым полям более чем одного класса. Для этого их можно сделать дружественными функциями.<br />
<span id="more-382"></span><br />
<strong>Перегрузка унарного оператора</strong><br />
Продолжим рассмотрение перегрузки оператора, проиллюстрировав ее перегрузкой одноместных операторов, типа  &#8220;!&#8221;, &#8220;++&#8221;, &#8220;~&#8221; и &#8220;[]&#8220;. Для этих целей разработаем класс clock, который может быть использован для хранения времени в виде дней, часов, минут и секунд. Будем развивать хорошо знакомые операции для clock:</p>
<pre><code>
class clock
{private:
  unsigned long int tot_sees, sees, mins, hours, days;
 public:
  clock(unsigned long int i); //конструктор и преобразование
  void print();               //форматированный вывод
  void tick();                //добавление одной секунды
  clock operator++(){ this->tick(); return (*this); }
};</code></pre>
<p>Этот класс перегружает префиксный оператор приращения. Сигнатурой для перегрузки совместимого постфиксного оператора приращения будет<br />
lock operator++(int). После перегрузки эта форма оператора приращения неявно вызывается с целым значением 0 в качестве реального целого параметра. Перегруженный оператор представляет собой функцию-член и может быть вызван со своим аргументом по умолчанию. Функция-член tlick добавляет одну секунду к неявному аргументу перегруженного оператора++.</p>
<pre><code>
inline clock::clock (unsigned long int i)
{tot secs = i;
 sees = tot sees % 60;
 mins = (tot_secs / 60) % 60;
 hours = (tot_secs / 3600) %60;
 days = tot_secs / 86400;
}

void clock::tick()
{clock temp (++tot sees) ;
 sees = temp.sees;
 mins = temp.mons;
 hours = temp.hours;
 days = temp.days;
}
</code></pre>
<p>Конструктор выполняет удобное преобразование из tot_secs в дни, часы, минуты и секунды. Например, поскольку в дне 86400 секунд, деление нацело на эту константу дает число дней. функция-член tick создает clock temp, который добавляет одну секунду к общему времени. Конструктор действует как функция преобразования, которая, собственно, и обновляет время.<br />
Перегруженный operator++() также обновляет неявную переменную clock и возвращает обновленное значение. Он может быт написан также, как и tick (), за исключением оператора </p>
<pre><code>
return (temp).
void clock::print ()
(cout<<days<<" d :"<<hours<<"h :"<<mins
     <<" m :"<<sees<<" s\n";
}

main()
{clock 11(59),t2(172799); 	//минуты-1 секунда и
//2 дня - 1 секунда
 cout<<"initial times are \n";
 tl.print();
 t2.print();
 ++tl; ++t2;
 cout<<"after one second times are\n";
 tl.print();
 t2.print();
}</code></pre>
<p>Результат вывода программы:<br />
initial times are Od:0h :0m :59s 1 d :23 h :59 m :59 s after one second times are Od:0h :lm:0s 2d:0h:0m:0s</p>
<p>Префиксную операцию ++ можно перегрузить следующим образом, используя friend-функцию<br />
friend clock operator++(clocks c1)<br />
 { c1.tick(); return (c1); }<br />
Заметьте, что, поскольку переменная clock должна увеличиться на одну секунду, мы вызываем ее по ссылке. Решение о выборе между представлением friend и функцией-членом обычно зависит от того, насколько необходимы и доступны операторы неявного преобразования. Явная передача аргумента, как в friend-функции, позволяет автоматическое его приведение.</p>
<p><strong>Перегрузка бинарного оператора</strong><br />
Продолжаем пример clock, демонстрируя перегрузку двухместных операторов. В основном, сохраняются те же принципы. Если двухместный оператор перегружается с использованием функции-члена, то в качестве своего первого аргумента он получает неявно переданную переменную класса, а второго - единственный из списка аргументов. При объявлении friend-функции или обычной функции в списке параметров определяют оба аргумента. Конечно, обычная функция не может обращаться к private членам.<br />
Напишем операцию сложения для типа clock.</p>
<pre><code>
class clock
{
 friend clock operator+(const clock&#038; cl,const clock&#038; c2);
}

clock operator+(const clock&#038; c1, const clock&#038; c2)
{clock temp(c1.tot sees + c2.tot sees);
 return (temp) ;
};</code></pre>
<p>Оба параметра явно определяются и являются кандидатами для преобразования назначением. Строка программы<br />
clock temp (c1.tot_secs + c2.tot secs);<br />
использует конструктор для преобразования выражения unsigned long int в значение clock.<br />
Используя это определение, имеем</p>
<p>int i = 5;<br />
clock с(900) ;<br />
...<br />
с + i //допустимо: i преобразовано в clock<br />
i + с //допустимо: i преобразовано в clock</p>
<p>В противоположность этому, перегрузим двухместный минус функцией-членом:</p>
<pre><code>
class clock
{
 clock operator-(const clock&#038; с);
};

clock clock::operator-(const clock&#038; c)
{
 clock temp( tot_secs - c.tot_secs);
 return (temp) ;
}</code></pre>
<p>Помните, что существует неявный первый аргумент. В него попадает некоторый используемый параметр. Это также может вызывать асимметричное поведение двухместных операторов.</p>
<p>int i = 5;<br />
clock с(900) ;<br />
c - i //допустимо: i преобразовано в clock<br />
с.operator-(i) //вызов функции<br />
i - с //недопустимо: i не относится к типу clock i.operator-(с) //недопустимая запись вызова функции<br />
Ясно видно, что при использовании вызова функции, переменная i не относится к типу clock и, следовательно, не "понимает" значение минуса.<br />
Определим операцию умножения как двухместную операцию с первым параметром unsigned long int и вторым параметром - переменной clock. Операция реализуется как friend-функция.</p>
<pre><code>
clock operator*(unsigned long int m,const clock&#038; c)
{
 clock temp(m * с.tot secs);
 return (temp);
}</code></pre>
<p>Такая реализация вынуждает операцию умножения иметь фиксированный порядок выполнения, зависящий от типа. Для избежания этого обычно пишется второй перегруженный функциональный оператор</p>
<p>clock operator*(const clock&#038; с, unsigned long int m);</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/peregruzhaemye-operatory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ДРУЖЕСТВЕННЫЕ (friend) ФУНКЦИИ И КЛАССЫ</title>
		<link>http://www.studcode.ru/archiv/druzhestvennye-friend-funkcii-i-klassy/</link>
		<comments>http://www.studcode.ru/archiv/druzhestvennye-friend-funkcii-i-klassy/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 19:29:13 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=380</guid>
		<description><![CDATA[Ключевое слово friend &#8211; спецификатор функции, который дает функции &#8211; не члену класса доступ к скрытым членам класса. Он используется для того, чтобы выйти за строгие рамки типизации и сокрытия данных в C++. Однако, должны быть весьма серьезные причины для выхода за пределы этих ограничений, поскольку от них зависит надежность программирования. Поэтому использование friend функций [...]]]></description>
			<content:encoded><![CDATA[<p>Ключевое слово friend &#8211; спецификатор функции, который дает функции &#8211; не члену класса доступ к скрытым членам класса. Он используется для того, чтобы выйти за строгие рамки типизации и сокрытия данных в C++. Однако, должны быть весьма серьезные причины для выхода за пределы этих ограничений, поскольку от них зависит надежность программирования. Поэтому использование friend функций в языке C++ спорно.<br />
Одна из причин их использования состоит в том, что некоторые функции нуждаются в привилегированном доступе более, чем к одному классу. Вторая причина &#8211; friend-функция передает все параметры через список параметров, и значение каждого из них подчинено преобразованию, совместимому с назначением. Такие преобразования применяются к явно переданным аргументам-классам и поэтому особенно полезны в случаях перегрузки оператора. Это будет показано в следующем разделе.<br />
<span id="more-380"></span><br />
Объявление friend (дружественной) функции должно появляться внутри объявления класса, которому она дружественна. Имени функции предшествует ключевое слово friend, и ее объявление может находится как в public, так и в private части класса, что не повлияет на значение. Функция-член одного класса может быть friend-функцией другого класса. Это происходит тогда, когда функция-член объявлена в friend классе с использованием оператора разрешения контекста для определения имени функции дружественного класса. Если все функции-члены одного класса являются friend-функциями второго класса, то это можно определить записью</p>
<pre><code>
friend class имя класса. 

Следующие объявления иллюстрируют синтаксис:
class tweedledee
{...
friend void alice(); //friend-функция
int Cheshire();      //функция-член
...
};

class tweedledum {
...
friend int tweedledee::Cheshire();
...
};

class tweedledumber
(...
 friend class tweedledee; //все функции-члены
...                         //tweedledee имеют доступ
};</code></pre>
<p>Рассмотрите класс matrix (см. упражнение 6.24) и класс vect (см. главу 6). Функция умножения вектора на матрицу, которые представлены этими двумя классами, может быть написана более эффективно, если будет иметь доступ к private членам обоих классов. Эта функция будет friend для обоих классов. В главе 6 безопасный доступ к элементам vect и matrix обеспечивался их соответствующими функциями-членами vect::element() и matrix::element(). Можно написать функцию, использующую этот тип доступа, которая будет умножать, не требуя статуса friend. Однако, необходимость в обращении к функциям и проверке выхода за пределы массива приведет к тому, что такая матрица будет умножаться исключительно неэффективно.</p>
<pre><code>
class matrix; //forward определение
class vect
{private:
  int* p;
  int size;
  friend vect mpy(const vect&#038; v, const matrix&#038; m);
 public:
  ...
};

class matrix
{ //хранит целые элементы
 private:
  int** base;
  int row_size, column_size;
  friend vect mpy(const vect&#038; v, const matrix&#038; m) ;
 public:
  ...
};

vect mpy(const vect&#038; v, const matrix&#038; m)
{if (v.size != m.row_size) //неверные размеры
 {cerr<<"multiply fai led - sizes incorrect "
      <<v.size<<" and " <<m.row_size<<endl;
  exit(1) ;
}
//использует привилегированный доступ к р в обоих классах vect ans(m.column_size)
{int i, j ;
 for (i = 0; i<=m.ub2(); ++i)
  {ans.р[i] = 0;
   for (j = 0; j<=m.ubl(); ++j)
     ans.p[i] += v.p[j] * m.base[j][i];
  }
 return (ans);
}</code></pre>
<p>Второстепенное значение требует предварительного описания класса matrix. Оно необходимо потому, что функция mpy должна появляться в обоих классах, и использует каждый класс как тип аргумента.<br />
Friend-функции можно рассматривать как часть общего интерфейса класса. Существует ряд ситуаций, в которых они могут быть альтернативой функциям-членам. Использование friend спорно, потому что они наруша-ют инкапсуляцию, окружающую private члены классов. Парадигма ООП утверждает, что объскгы (в C++ они — переменные класса) доступны через их public члены. Только функции-члены должны иметь доступ к скрытой реа-лизации АТД. Это - ясный и строгий принцип проектирования. Friend-функция, однако, находится на самой его границе, поскольку имеет доступ к private членам, но сама при этом не является функцией-членом. С ее помощью можно реализовать быстрый код для доступа к подробностям реализации класса. Применяя такой механизм можно легко нарушить режим работы. Однако, как в предыдущем примере, где основной целью была эффективность, и в некоторых других ситуациях, friend-функции могут оказаться полезными.<br />
<strong><br />
ДРУЖЕСТВЕННЫЕ КЛАССЫ</strong><br />
Класс может определять объекты, необходимые в качестве внутренней подробности для других классов. Чтобы сохранять анонимность, такой класс должен быть частным. Класс, который обрабатывает эти частные объекты, должен быть с ними в отношениях дружественности.<br />
Вспомните тип string, определенный с семантикой подсчета ссылок в главе 6. Индивидуальные значения строки, в конечном счете, ссылались на объекты типа str_obj. Это отделение позволяло одному экземпляру str_obj, требовавшему большого количества байт памяти, использоваться большим количеством экземпляров string. Мы можем преобразовать этот пример, используя отношение дружественности для сохранения секретности класса s tr_obj.</p>
<pre><code>
class str_obj
{private:
  friend class string;
  friend class string iterator;
  friend ostream&#038; operator«(ostream&#038; out,const string&#038; str);
  int len, ref_cnt;
  char* s;
  str_obj ():len(0), ref_cnt(l) { s = new char[l]; }
                                         //инициализаторы
  str_obj(int n):len(n), ref_cnt(l) { s = new char[n + 1]; }
  str obj(const char* p):ref cnt(l)
                { len = strlen(p); s = new char[len + 1];
  strcpy(s, p); } ~str_obj () { delete [] s; }
};</code></pre>
<p>Преимуществом этого проекта из двух классов будет увеличение гибкости дальнейшего отделения подробностей реализации от кода пользователя.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/druzhestvennye-friend-funkcii-i-klassy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Полиморфизм. АНАЛИЗ ПРОГРАММЫ overloading</title>
		<link>http://www.studcode.ru/archiv/polimorfizm-analiz-programmy-overloading/</link>
		<comments>http://www.studcode.ru/archiv/polimorfizm-analiz-programmy-overloading/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 19:08:45 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=375</guid>
		<description><![CDATA[Конструктор
complex (double r) { real = r; imag =0; }
обеспечивает преобразование из double в complex. Функция-член
operator double()
      {return (sqrt(real * real+imag * imag));}
обеспечивает преобразование из complex в double.
inline int greater(int i, int j){return (i>j?i:j);}
inline double greater(double x, double y)
            [...]]]></description>
			<content:encoded><![CDATA[<p>Конструктор<br />
complex (double r) { real = r; imag =0; }<br />
обеспечивает преобразование из double в complex. Функция-член<br />
operator double()<br />
      {return (sqrt(real * real+imag * imag));}<br />
обеспечивает преобразование из complex в double.<br />
inline int greater(int i, int j){return (i>j?i:j);}<br />
inline double greater(double x, double y)<br />
                               {return (x>y?x:y);}<br />
inline complex greater(complex w, complex z)<br />
                               {return (w>z?w:z);}<br />
Перегружены три отдельных функции. Из них наиболее интересная имеет тип переменных из списка параметров и возвращаемый тип complex. Функция-член преобразования operator double требуется для оценки условия w > 2. Complex переменные w и z преобразуются в double. Далее в этой главе будет обсуждена перегрузка операторов — конструкция, которая позволяет обеспечивать новые значения для существующих операторов C++. Для возвращаемого типа нет необходимости в преобразовании.<br />
<span id="more-375"></span><br />
w.assign(x,y) ;<br />
z.assign(i, j);</p>
<p>Первый вызов функции-члена assign требует преобразования аргумента float x в double. Double аргумент у в преобразовании не нуждается. Оба параметра второго вызова имеют тип int, требующий преобразования. Целые аргументы совместимы с double по назначению.</p>
<p>cout<<&#8221;compare &#8220;<<i<<&#8221; and &#8220;<<j<<&#8221; greater is &#8221;<br />
    <<greater(i, j)<<endl;<br />
cout<<&#8221;compare &#8220;<<x<<&#8221; and &#8220;<<y<<&#8221; greater is &#8221;<br />
    <<greater(x, y)<<endl;</p>
<p>Первый оператор выбирает первое определение greater по правилу соответствия. Второй оператор выбирает второе определение greater, так как использовано стандартное расширяющее преобразование из float в double. Значение переменной х расширяется до double.<br />
cout<<&#8221; greater is &#8220;<<greater(y, double(z))<<endl;<br />
Второе определение greater выбирается из-за точного соответствия правилу. Для избежания неоднозначности необходимо явное преобразование double (z). Если вместо этого будет использоваться вызов функции</p>
<p>greater(y, z);    //ERROR<br />
то он будет иметь два доступных преобразования, достигших соответствия. Преобразование параметра у из double в complex, определяемое пользователем, соответствует третьему определению. Преобразование параметра z из complex в double, также определяемое пользователем, соответствует второму определению. Но второе определение функции имеет лучшее соответствие для первого параметра, между тем как третья функция имеет лучшее соответствие для второго параметра. Это нарушает требование о том, что &#8220;Максимальное соответствие должно быть уникально. Оно должно быть лучше всего для, по крайней мере, одного параметра и одинаково хорошо для всех остальных параметров.&#8221;<br />
Определению три точно соответствует:</p>
<p>zmax = greaterfw, z);</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/polimorfizm-analiz-programmy-overloading/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Полиморфизм</title>
		<link>http://www.studcode.ru/archiv/polimorfizm/</link>
		<comments>http://www.studcode.ru/archiv/polimorfizm/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 18:48:43 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=372</guid>
		<description><![CDATA[Полиморфизм &#8211; средство для придания различных значений одному и тому же сообщению в зависимости от типа обрабатываемых данных.
Преобразование &#8211; это явное или неявное изменение значения в зависимости от типа. Преобразования обеспечивают форму полиморфизма.
Перегрузка функций дает одному и тому же имени функции различные значения. Одно и то же имя имеет различные интерпретации, которые зависят от выбора [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Полиморфизм</strong> &#8211; средство для придания различных значений одному и тому же сообщению в зависимости от типа обрабатываемых данных.<br />
<strong>Преобразование</strong> &#8211; это явное или неявное изменение значения в зависимости от типа. Преобразования обеспечивают форму полиморфизма.<br />
Перегрузка функций дает одному и тому же имени функции различные значения. Одно и то же имя имеет различные интерпретации, которые зависят от выбора функции. Выбранная функция удовлетворяет алгоритму соответствия сигнатур для C++. Такая форма полиморфизма называется (ad hoc) специальным полиморфизмом. Эта глава, в основном, посвящена перегрузке, в первую очередь -перегрузке оператора и преобразованию типов данных.<br />
Операторы перегружаются и выбираются на основании алгоритма соответствия сигнатур. Перегрузка операторов придает им новые значения. Например, выражение а + b имеет различные значения, в зависимости от типов переменных а и b. Перегрузка оператора + для типов, определяемых пользователем, позволяет использовать их в дополнительных выражениях в большинстве случаев так же, как и встроенные типы. Выражение а + b может означать конкатенацию строк, сложение комплексных или целых чисел в зависимости от того, какими были переменные — АТД string, АТД complex или встроенного типа int. При определении функции преобразования допустимы также выражения смешанного типа. Кроме того, в этой главе обсуждаются friend-функции и то, насколько они важны для перегрузки операторов.<br />
Один из принципов ООП состоит в том, что определяемые пользователем типы должны иметь те же привилегии, что и встроенные типы. Пользователю необходимо одинаковое удобство при использовании как встроенных, так и внешних типов. Чем точнее изготовитель достигает такого результата, тем адекватнее язык ООП использованию. Типы, встроенные в базовый язык, могут смешиваться в выражениях, поскольку это удобно, но, с другой стороны, при этом обременительно определять последовательность необходимых преобразований.</p>
<p><span id="more-372"></span></p>
<p><span style="text-decoration: underline;"><strong>Преобразования, определяемые классом</strong></span><br />
Явное преобразование типов выражения применяется тогда, когда неявное преобразование нежелательно или когда без него выражение недопустимо. Одна из задач C++ &#8211; интеграция АТД и встроенных типов. Чтобы достигнуть этого, существует механизм функции-члена, обеспечивающей явное преобразование.<br />
Функциональная запись в такой форме<br />
Имя типа (выражение)<br />
эквивалентна приведению. Тип должен быть выражаем как идентификатор. Таким образом, два выражения<br />
х = float(i); //функциональная запись C++ х = (float) i;<br />
эквивалентны. Выражение<br />
р = (int*) q; //допустимое приведение<br />
не может непосредственно функционально выражаться как<br />
р = int* (q); //запрещено<br />
Однако, для этого может использоваться typedef.<br />
typedef int* int_ptr;<br />
р = int_ptr (q) ;<br />
Поэтому функциональная запись более предпочтительна. Конструктор с одним аргументом фактически представляет собой преобразование из типа аргумента к типу конструируемого класса. В разделе 6.4<br />
строковый тип имел такой конструктор:</p>
<pre><code>
string (const char* р)
{len=strlen(p);
s=new char[len+1];
strcpy(s,р);
}</code></pre>
<p><span style="text-decoration: underline;"><strong>Конструкторы как преобразования</strong></span><br />
Конструкторы с одиночным параметром автоматически являются функциями преобразования. Преобразования представляют собой аспект полиморфизма (см. главу 7) и упрощают код пользователя. Проанализируем следующий класс, цель которого состоит в том, чтобы печатать невидимые символы с их обозначением ASCII; например, код 07 (восьмеричный) signal или bel.<br />
//печатные символы ASCII</p>
<pre><code>
class pr_char
{private:
int c;
static char* rep[128];
public:
pr_char(int i = O): c(i % 128) {}
void print() { cout&lt;
char* pr_char::rep[128]={"nul","soh","stx",// и т. д.
...
"w", "x", "у", "z", "{", "|",")","~", "del"};
main()
{int i;
pr char c;
for (i = 0; i &lt; 128; ++i ) {
c = i; // так же с = pr char(i );	или с =	(pr char)i;
c.print() ;
cout&lt;
} }</code></pre>
<p>Конструктор выполняет автоматическое преобразование из целых чисел в pr char. Это делается с помощью оператора<br />
с = i;<br />
в цикле. Возможно использовать и явное приведение. Чрезмерное применение неявньк преобразований может привести к написанию небезопасного кода неизвестного типа.<br />
Преобразованиям посвящен материал главы 7. Одна из причин того, что ООП требует использования преобразований, состоит в том, что типы, определяемые пользователем, должны выглядеть и ощущаться как встроенные типы.<br />
Представленное выражение — автоматическое преобразование типа от char* к string. Оно доступно как явно, так и неявно. Явно оно используется или как операция преобразования, или в приведении, или в функцио-нальной форме. Таким образом, возможны два рабочих варианта кода:<br />
string s;<br />
char* logo = &#8220;Geometries Inc&#8221; ;<br />
s=string(logo); //выполняет преобразование, затем присвоение<br />
И<br />
s = logo; //неявный вызов преобразования<br />
Данный код &#8211; преобразование из уже определенного типа к типу, определяемому пользователем. Однако, пользователь не может добавлять конструкторы встроенных типов, таких как int или double. В примере со строкой может возникнуть необходимость в преобразовании из строки в char*, что может быть выполнено с помощью определения специальной функции преобразования внутри класса string следующим образом:<br />
operator char*(){return (s);}</p>
<p>Общая форма записи такой функции-члена:</p>
<p>operator тип{) {. . .}</p>
<p>Такая функция преобразования должна быть нестатической функцией-членом без возвращаемого типа и с пустым списком аргументов. Преобразования происходят неявно в выражениях присвоения, при передаче параметров функциям, и в значениях, возвращаемых функциями.<br />
Преобразующая функция-член в форме А::operatorВ() и конструктор в форме В::В(const A&amp;) обеспечивают преобразование из типа объекта А в тип объекта В. Наличие обоих одновременно может приводить к ошибкам неоднозначности.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/polimorfizm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Создание классов.</title>
		<link>http://www.studcode.ru/archiv/sozdanie-klassov/</link>
		<comments>http://www.studcode.ru/archiv/sozdanie-klassov/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 20:49:39 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=370</guid>
		<description><![CDATA[Создание объекта
Для объекта требуется наличие памяти и некоторое начальное значение. Базовый язык обеспечивает это условие с помощью объявлений, которые в то же время являются определениями. В большинстве случаев, когда мы описываем объявления, мы подразумеваем объявления-определения. Например, в

void foo()
{int n = 5;
double z[10]={ 0.0};
struct gizmo { int i, j; } w = {3,4};
}
все объекты создаются при [...]]]></description>
			<content:encoded><![CDATA[<p><span style="text-decoration: underline;"><strong>Создание объекта</strong></span><br />
Для объекта требуется наличие памяти и некоторое начальное значение. Базовый язык обеспечивает это условие с помощью объявлений, которые в то же время являются определениями. В большинстве случаев, когда мы описываем объявления, мы подразумеваем объявления-определения. Например, в</p>
<pre><code>
void foo()
{int n = 5;
double z[10]={ 0.0};
struct gizmo { int i, j; } w = {3,4};
}</code></pre>
<p>все объекты создаются при входе в блок, при вызове foo(). Эта типичная реализация во время выполнения использует стек системы. В каждом случае создание и инициализацию этих объектов обеспечивает система. Освобождение памяти происходит автоматически, после выхода из foo ().<br />
Создавая сложные агрегаты, пользователь нуждается в подобном управлении объектом, определенным как класс. Для класса необходим механизм, который определяет его поведение при создании и разрушении таким образом, что пользователь может использовать объекты аналогично встроенным типам.<br />
<span id="more-370"></span> Конструктор &#8211; функция-член с таким же именем, как у класса. Он создает значения типа класса. Этот процесс включает в себя инициализацию членов-данных и, часто, распределение свободной памяти с использованием new.<br />
Деструктор &#8211; функция-член, имя которой &#8211; то же, что и имя класса, с предшествующим ему символом ~ (тильда). Обычно он применяется для того, чтобы уничтожать значения типа класса, чаще всего с использованием delete.<br />
Из этих двух специальных функций-членов более сложными являются конструкторы. Они могут быть перегружены и могут получать параметры, чего нельзя сказать о деструкторах. Конструктор вызывается в том случае, когда в определении использован связанный тип. Он вызывается также тогда, когда доя передачи значения функции используется вызов по значению. Конструкторы и деструкторы не имеют возвращаемого типа и не могут использовать операторы return (выражение). Вызов деструктора происходит тогда, когда должен быть уничтожен объект класса; обычно после выхода из блока или из функции деструкторы вызываются неявно.<br />
<span style="text-decoration: underline;"><strong>Классы с конструкторами</strong></span><br />
Простейший случай использования конструкторов &#8211; для инициализации. В этом и последующих разделах приведены несколько примеров, в которых конструкторы используются для инициализации значений данных-членов класса. Первый пример представляет собой реализацию типа данных mod_int для хранения чисел, которые вычисляются по модулю.<br />
//модули чисел и инициализация с помощью конструктора</p>
<pre<code>>
#include
const int modulus = 60;
class mod_int
{private:
int v;
public:
mod_int(int i) { v = i t modulus; }
void assign(int i) { v = i t modulus; }
void print() { cout&lt;&lt;&lt;"\t"; }
};</code></pre>
<p>Целое число v ограничивается значениями 0, 1, 2, &#8230;, modulus &#8211; 1. Программист должен добиться, чтобы это ограничение обеспечивалось всеми функциями-членами.<br />
Функция-член mod_int::mod_int() &#8211; конструктор. У нее нет возвращаемого типа. Она вызывается во время объявления объектов типа mod_int. Эта функция имеет один параметр. При вызове для нее необходимо выражение, совместимое по присвоению с параметром типа int, который необходимо передать. Тогда конструктор создает и инициализирует объявленную переменную (экземпляр объекта).<br />
Рассмотрим примеры объявлений, где используется этот тип:<br />
mod_int a(0);   //a.v = 0;<br />
mod_int b(61); //b.v = 1;<br />
но не<br />
mod_int а;      //без списка параметров<br />
Используя этот тип, можно написать код для преобразования секунд в минуты и секунды следующим образом.</p>
<pre><code>
main()
{int seconds = 400;
mod int z(seconds);
cout&lt;&lt;&lt;" секунд эквивалентно "&lt;&lt;&lt; " минут";
z.print();
cout&lt;&lt;" секунд\п";
}</code></pre>
<p><span style="text-decoration: underline;"><strong>Конструктор по умолчанию</strong></span><br />
Конструктор, не требующий параметров, называется конструктором по умолчанию. Это может быть конструктор с пустым списком параметров или конструктор, в котором все параметры имеют значения по умолчанию. Он специально предназначен для инициализации массивов объектов класса.<br />
Часто бывает удобно перегружать конструктор несколькими объявлениями функций. В нашем примере, было бы желательно чтобы по умолчанию v имела значение 0. Добавляя конструктор по умолчанию<br />
mod_int() {v = 0; }<br />
в качестве функции-члена mod int, можно получить следующие объявления:<br />
mod_int si, s2; //оба инициализируют private член v нулем mod_int d[5];   //массив правильно инициализирован</p>
<p>В обоих объявлениях вызывается конструктор с пустым списком параметров.<br />
Если класс не имеет конструктора, то массивы объектов конкретного типа распределяются системой автоматически. Если класс имеет конструктор, но не имеет конструктора по умолчанию, то распределение массива будет синтаксической ошибкой.<br />
Обратите внимание, что наш пример mod_int мог иметь один конструктор, служащий не только общим инициализатором, но и конструктором по умолчанию.<br />
inline mod_int::mod int(int i=0){v=i%modulus; }</p>
<p><span style="text-decoration: underline;"><strong>Конструктор копирования</strong></span><br />
Допустим, необходимо исследовать стек и посчитать число вхождений данного символа. Для этого можно многократно выталкивать стек, проверяя каждый элемент, до тех пор, пока стек не будет пуст. Но как выйти из положения, если содержимое стека необходимо сохранить. Это можно сделать с помощью вызова параметров по значению.</p>
<pre><code>
int cnt_char(char с, stack s)
{int count = 0;
while (!s.empty())
count += (c == s.pop());
return (count);
}</code></pre>
<p>Семантика вызова по значению требует, чтобы локальная копия типа параметра создавалась и инициализировалась от значения выражения, переданного как фактический параметр. Для этого необходим конструктор копии (constructor copy). Компилятор предоставляет такой конструктор по умолчанию. Его сигнатура:<br />
stack::stack(const stacks);<br />
Компилятор производит копирование путем почленной инициализации. Для сложных агрегатов, члены которых сами по себе являются указателями, такой способ не всегда эффективен. В большинстве случаев, указатель является адресом объекта, удаляемого при выходе из контекста. Однако, код, в котором производится действие по дублированию значения указателя, а не объекта, на который он указывает, может быть ошибочным. Дело в том, что удаление воздействует на другие экземпляры, все еще предполагающие, что объект существует. Поэтому важно, чтобы класс имел свою собственную явно определенную копию конструктора.<br />
//конструктор копии для стека символов</p>
<pre><code>
stack::stack(const stacks str) //конструктор копии
{s = new char[str.max len];
max len = str.max_len;
top = str.top;
memcpy(s, str.s, max_len);
}</code></pre>
<p><span style="text-decoration: underline;"><strong>Инициализатор конструктора</strong></span><br />
Существует определенная синтаксическая конструкция для инициализации подэлементов объектов с конструкторами. Инициализаторы для структуры и членов класса могут определяться списком после списка параметров конструктора, через запятую и перед телом кода. Мы можем переписать предыдущий пример следующим образом:<br />
//Конструктор копии для стека символов</p>
<pre><code>
stack::stack(const stacks str)
:max_len(str.max len), top(str.top)
{s = new char[str.max_len];
memcpy(s, str.s, max len);
}</code></pre>
<p>Обратите внимание на то, как инициализация заменяет присвоение. Индивидуальные члены должны инициализироваться так:<br />
имя члена (список выражений)<br />
Если члены сами по себе являются классами с конструкторами, то список выражений должен быть согласован с соответствующей сигнатурой конструктора для правильного вызова перегруженного конструктора.<br />
Такая форма инициализации члена требуется тогда, когда нестатический член является const или ссылкой.<br />
<span style="text-decoration: underline;"><strong>Классы с деструкторами</strong></span><br />
Деструктор — это функция-член с таким же именем, как имя класса, перед которым стоит знак тильда. Они почти всегда вызываются неявно, обычно при выходе из блока, в котором был объявлен объект. Они также вызываются при вызове оператора delete для указателя на объект, имеющий деструктор, или в том случае, когда необходимо удалить объект, вложенный в удаляемый.<br />
Расширим наш пример стека деструктором.</p>
<pre><code>
//Реализация стека с конструктором и деструктором
class stack
{private:
enum { EMPTY = -1} ;
char* s;
int max len;
int top; public:
public:
stack();                  //конструктор по умолчанию
stack(int size)
{ s = new char[size];max_len = size; top = EMPTY; }
stack(const stacks str)  //конструктор копии
stack(int size, const char str[]);
~stack() { delete [] s; } //деструктор
};</code></pre>
<p>Внешний интерфейс этого класса остается таким же. Иными словами, все public функции-члены играют те же роли, что и прежде. Разница в том, что при вькоде из блока и функции неявно вызывается деструктор для освобождения той памяти, которая больше не доступна. Это &#8211; хороший стиль программирования, так как программы могут использовать меньшее количество доступной памяти.</p>
<p><span style="text-decoration: underline;"><strong>ПРИМЕР: Динамически размещаемые строки</strong></span><br />
В C++ отсутствует встроенный строчный тип. Строки представляются как указатели на char, и это влияет на манипуляции с ними. В таком представлении конец строки обозначается &#8220;\0&#8243;. Основной недостаток этого заключается в том, что большинство базовых манипуляций со строками зависят от их длин. Когда длина строки известна, эффективность строковых операций значительно выше.<br />
В этом разделе описывается полезный строковый АТД, хранящий длину строки в виде private. Тип динамически распределяется и способен представлять строки произвольной длины. Для инициализации и распределения памяти под строки составлен ряд конструкторов. Набор операций над строками закодирован как функции-члены. Для управления основным представлением строковых указателей применяются библиотечные функции из string.h.<br />
// реализация динамически распределяемых строк.</p>
<pre><code>
#include
#include
class string
{private:
char* s;
int len;
public:
string() { s = new char[l]; s[0] = 0; len = 0; }
string(const strings str); //конструктор копии
string(const char* p)
{len=strlen(p); s=new char[len+1]; strcpy(s,p);}
~string() { delete [] s; }
void assign(const strings str);
void print() const { cout&lt;&lt;
void concat(const st<span style="text-decoration: line-through;">ri</span>ngs a, const strings b);
};

void sring::string(const strings str)
{len = str.len;
s = new char[len + 1] ;
strcpy(s, str.s) ;
}

void string::assign(const	string&amp; str)
{if (this == &amp;str) return;
else delete [] s; // восстановление	старой строки
len = str.len;
s = new char[len + 1];
strcpy(s, str.s);
}

void string::concat(const	string&amp; a,const string&amp; b) {len = a.len + b.len;
delete [] s;
s = new char[len + 1];
strcpy(s, a.s);
strcat(s, b.s);
}</code></pre>
<p>Этот тип позволяет объявлять строки, определять копирование одной строки в другую, печатать строку и объединять две строки. Скрытое представление &#8211; указатель на char, имеет переменную len, в которой хранит теку-щую длину строки. Конструкторы распределяют все динамически из свободной памяти.<br />
<span style="text-decoration: underline;"><strong>АНАЛИЗ КЛАССА string</strong></span><br />
string(){s=new char[1]; s[0] = 0; len = 0; }<br />
string(const string&amp; str); //конструктор копии string(const char* p)<br />
{len=strlen(p);s=new char[len+1]; strcpy(s,p);}</p>
<p>Рассмотрим три перегруженных конструктора. Первый &#8211; с пустыми параметрами по умолчанию. Он используется для объявления массива строк. Второй &#8211; конструктор копии. Третий включает указатель на параметр char, который может использоваться .для преобразования представления char* строк в тип нашего class. Он использует две библиотечных функции: strlen и strcpy. Распределяем один дополнительный символ для хранения символа &#8220;\0&#8243; конца строки (EOL), хотя этот символ не считается strlen. Конструктор копии объясняется ниже.</p>
<p>~string() { delete [] s; }</p>
<p>Деструктор автоматически возвращает обратно память, распределенную для строк, для того, чтобы освободить память для повторного использования. Пара пустых квадратных скобок в delete стоит потому, что использовалось распределение для массива. Оператору delete [] объем памяти, связанный с указателем s, известен как базовый адрес массива.<br />
string::string(const string&amp; str)<br />
{len = str.len;<br />
s = new char[len+1];<br />
strcpy(s, str.s);<br />
}<br />
Представленный конструктор копии используется для того, чтобы выполнить копирование одного значения строки в другое, когда строка<br />
• инициализируется другой строкой;<br />
• передается в виде параметра в функцию;<br />
• возвращается в виде значения функции.<br />
В C++, если такой конструктор отсутствует, то описанные операции представляют собой почленное переназначение. Это обсуждается далее в разделе 6.10.</p>
<pre><code>
void string::assign(const strings str)
{
if (this == Sstr) return;
else
delete [] s; //восстанавливает старое значение
len = str.len;
s = new char[len + 1];
strcpy(s, str.s);
}</code></pre>
<p>Семантика назначения основывается на &#8220;семантике глубокого копирования&#8221; (deep copy semantics). При копировании необходимо контролировать, чтобы оно не производилось над одной и той же строкой. Каждый раз, при копировании значения строки, происходит физическое копирование значения с использованием strcopy().</p>
<pre><code>
void string::concat(const string&amp; a, const string&amp; b)
{len=a.len+b.len;
delete [] s;
s = new char[len+1];
strcopy(s, a.s);
strcopy(s, b.s);
}</code></pre>
<p><strong><span style="text-decoration: underline;">ЧЛЕНЫ, ИМЕЮЩИЕ ТИП КЛАССА</span></strong><br />
В этом разделе тип vect используется как часть нового класса. Необходимо хранить множества значений для каждого индекса. Например, может возникнуть потребность хранить возраст, вес и рост группы лиц. Для этого можно сгруппировать вместе три массива внутри нового класса.</p>
<pre><code>
#include "vect.h"
class multi_v
{public:
vect a, b, c;
multi_v(int i): a(i), b(i), c(i) {}
};</code></pre>
<p>В классе есть три члена vect и конструктор, который имеет пустое тело и список (через запятую) вызываемых конструкторов. Они выполняются с целым аргументом i, создавая три объекта класса а, b и с. Члены типа class инициализируются в порядке объявления.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/sozdanie-klassov/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ФУНКЦИИ-ЧЛЕНЫ static И const</title>
		<link>http://www.studcode.ru/archiv/funkcii-chleny-static-i-const/</link>
		<comments>http://www.studcode.ru/archiv/funkcii-chleny-static-i-const/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 20:37:36 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=368</guid>
		<description><![CDATA[C++ допускает функции-члены static и const. Их реализацию можно понять в терминах доступа через указатель this. Обычная функция-член вызывается как
object.mem ( i, j, k) ;
Она имеет явный список параметров i, j, k и неявный список параметров, который состоит из членов object. Неявные параметры можно представить как список параметров, доступных через указатель this. Напротив, статическая (static) [...]]]></description>
			<content:encoded><![CDATA[<p>C++ допускает функции-члены static и const. Их реализацию можно понять в терминах доступа через указатель this. Обычная функция-член вызывается как<br />
object.mem ( i, j, k) ;<br />
Она имеет явный список параметров i, j, k и неявный список параметров, который состоит из членов object. Неявные параметры можно представить как список параметров, доступных через указатель this. Напротив, статическая (static) функция-член не может обращаться к любому из членов, используя указатель this. Функция-член const не может изменять неявные параметры. Следующий пример иллюстрирует эти различия:<br />
<span id="more-368"></span><br />
//вычисление жалованья с использованием<br />
//функций-членов static и const</p>
<pre><code>
«include <iostream.h>
class salary
{
    int b_sal;
    int your bonus;
    static int all_bonus; //объявление public:
    initfint b) { b_sal = b; }
    void calc_bonus(double perc){your_bonus=b_sal*perc;}
    static void reset_all(int p){all_bonus=p;}
    int comp tot() const
           { return (b_sal + your_bonus + all_bonus) ;
};
int salary::all_bonus = 100; //объявление
main()
{
 salary wl, w2;
 wl.init(lOOO) ;
 w2.init(2000) ;
 wl.calc bonus(0.2);
 w2.calc_bonus(0.15) ;
 salary::reset_all(400);
 cout<<"wl "<<wl.comp_tot()<<" w2 "<<w2.comp_tot()<<endl;
}</code></pre>
<p><strong>Указатели на членов</strong><br />
Указатели на членов указывают на нестатические поля или методы (включая виртуальные) любого объекта класса (Указатели на статические поля - это обычные указатели).</p>
<pre><code>
struct Point
{int x, у;
 void Set( int X, int Y) { x = X, у = Y; }
 static int Z;
};
int Point::Z=0;
int *pZ=&#038;Point::Z;
Pointa а, *ра = &#038;а;
// pint - это указатель на любой int член класса Point
int Polnt::*pint = &#038;Point::x; // В этом случае х
pmf2i - это указатель на любую функцию-член  класса Point,
принимающую два аргумента типа  int и возвращающую void
void (S::*pmf2i) (int, int) = &#038;S::Set;
++( a.*pint); 	// Инкремент x
++( pa->*pint); 	// Снова инкремент х
(*pZ)++;	// Инкремент Z
(a.*pmf2i)(1,1); 	// Вызов SetO
( pa->*pmf2i) ( 2, 2 ); // Вызов Set () через pa
</code></pre>
<p>•	Неявно преобразуются в указатели на членов производных классов, если это необходимо (см. "Указатели" в разделе "Производные классы").<br />
•	Не преобразуются неявно в указатели на void, как обычные указатели.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/funkcii-chleny-static-i-const/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ВЛОЖЕННЫЕ КЛАССЫ. УКАЗАТЕЛЬ this</title>
		<link>http://www.studcode.ru/archiv/vlozhennye-klassy-ukazatel-this/</link>
		<comments>http://www.studcode.ru/archiv/vlozhennye-klassy-ukazatel-this/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 20:34:04 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=366</guid>
		<description><![CDATA[Классы могут бьпъ вложенными. Вложенные классы, подобно вложенным блокам, имеют внутреннюю и внешнюю спецификацию. Правила для вложенных классов изменились по отношению к правилам, существовавшим в С, где разрешались вложенные struct, но имена внутренних structs были видимы извне. Это было концептуально нелогично и теперь исправлено.
Следующие вложенные классы иллюстрируют текущие правила C++:

char с;     [...]]]></description>
			<content:encoded><![CDATA[<p>Классы могут бьпъ вложенными. Вложенные классы, подобно вложенным блокам, имеют внутреннюю и внешнюю спецификацию. Правила для вложенных классов изменились по отношению к правилам, существовавшим в С, где разрешались вложенные struct, но имена внутренних structs были видимы извне. Это было концептуально нелогично и теперь исправлено.<br />
Следующие вложенные классы иллюстрируют текущие правила C++:</p>
<pre><code>
char с;       //внешняя область(контекст)
class X //внешнее объявление класса char с;
 { class У //внутреннее объявление класса
    {char с;
     void foo (char e) { ::с=Х::с==с=е; }
    }
 }</code></pre>
<p><span id="more-366"></span><br />
В классе Y, функция-член foo, используя с, ссылается на переменную Х : : Y: : с. С использованием оператора разрешения контекста доступны три перемсннык с именем.<br />
Кроме того, внутри блоков могут создаваться полностью локальные классы. Эти определения недоступны вне контекста их локального блока.</p>
<pre><code>
void foo()
{ class local {	. .	. }	х; //все что угодно
}
local у; //запрещено: local	- внутри контекста foo()
</code></pre>
<p>Обратите внимание, что C++ дает вам возможность создавать вложенные функции-члены с использованием вложенных классов. Это &#8211; ограниченная форма вложенности функций. Функции-члены должны определяться внутри локального класса и на них нельзя ссылаться вне этого контекста. Как и в С, обычные вложенные функции запрещены.<br />
Static-ЧЛЕНЫ<br />
Члены-данные могут быть объявлены с модификатором класса памяти static. Член-данные, который объявлен static, совместно используется всеми переменными этого класса и хранится уникально, в одном месте. Поэтому возможная форма обращения к нему<br />
имя класса: : идентификатор</p>
<p>обеспечивается тем, что он имеет видимость public. Это &#8211; дальнейшее использование оператора разрешения контекста. Статический член глобального класса должен явно определяться в контексте файла. Например:</p>
<pre><code>
enum boolean { false, true};
class str
 { char s [100];
  public:
   static boolean read_only;   //объявленный	в
                               //определении	класса
   static int count strings;
   void print();
   void assign(const char*) ;
 };
int str::count strings;           //определено
boolean str: : read only = false; //определено с явной
                                  //инициализацией
</code></pre>
<p><strong>УКАЗАТЕЛЬ this</strong><br />
Ключевое слово this представляет собой неявно определенный указатель на сам объект. Он может быть использован только для не статической функции-члена. Ссылка на сам объект не изменяет того факта, что this является указателем. Следовательно,<br />
•   this->имя члена указывает на объект, членом которого он является;<br />
• *this представляет собой сам объект и, в зависимости от контекста, может быть лево- или правосторонней величиной;<br />
•   this представляет собой адрес объекта.<br />
Простая иллюстрация его использования:</p>
<pre><code>
//использование указателя this
#include <iostream.h>
class c_pair
{ char cl,c2 ;
 public:
  void init(char b) { c2 =b; cl = 1+b; }
  c_pairs increment() { cl++; c2++; return(*this); )
  void* where am I() { return (this); }
  void print() { cout<<cl<<c2<<"\t"; }
};
main () {
 c_pair a, b, c;
 a.init('A') ;
 b.init('B') ;
 c.initf'D');
 a.print() ;
 cout<<" is at "<<a. where am I ()<<endl;
 b.print() ;
 cout<<" is at "<<b.where am I()<<endl;
 с.increment().print() ;
 cout<<" is at "<<c.where am I()<<endl;

}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/vlozhennye-klassy-ukazatel-this/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>КЛАССЫ. Оператор разрешения контекста : :</title>
		<link>http://www.studcode.ru/archiv/klassy-operator-razresheniya-konteksta/</link>
		<comments>http://www.studcode.ru/archiv/klassy-operator-razresheniya-konteksta/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 20:19:19 +0000</pubDate>
		<dc:creator>sleepes</dc:creator>
				<category><![CDATA[Системное программирование]]></category>
		<category><![CDATA[лекции]]></category>
		<category><![CDATA[С++]]></category>

		<guid isPermaLink="false">http://www.studcode.ru/?p=364</guid>
		<description><![CDATA[Классы в C++ представляются ключевым словом class. Они представляют собой форму struct, у которой спецификация доступа по умолчанию &#8211;private. Таким образом, struct и class могут использоваться поочередно с соответствующей спецификацией доступа.
Следующий АТД для комплексных чисел использует struct:

struct complex
 {void assign(double r, double i);
  void print(){cout]]></description>
			<content:encoded><![CDATA[<p>Классы в C++ представляются ключевым словом class. Они представляют собой форму struct, у которой спецификация доступа по умолчанию &#8211;private. Таким образом, struct и class могут использоваться поочередно с соответствующей спецификацией доступа.<br />
Следующий АТД для комплексных чисел использует struct:</p>
<pre><code>
struct complex
 {void assign(double r, double i);
  void print(){cout<<real<<"+"<<imag; "i";}
 private:
  double real, imag;
 };
void complex::assign(double r, double	i = 0.0)
{
 real = r ;
 imag = i;
}</code></pre>
<p><span id="more-364"></span><br />
Вот эквивалентное представление, но с помощью класса:</p>
<pre><code>
class complex {
double real, imag;
public:
void assign(double r, double i);
void print(){cout<<real<<"+"<<imag<<"i";}
};
void complex::assign(double r, double i = 0.0)
{
  real = r ;
  imag = i ;
}</code></pre>
<p>Обратите внимание, что единственное отличие - в использовании ключевых слов public и private. Возможно также такое написание:</p>
<pre><code>
class complex
 {private:  // один из способов сделать	его явным
   double real, imag;
  public:
   void assign(double r, double i);
   void print(){cout<<real<<"+"<<imag<<"i"; }
};</code></pre>
<p>Это наш стиль - отдавать предпочтение class по отношению к struct, при условии, что все члены не должны обрабатываться как publiс.<br />
<strong>Область видимости класса</strong><br />
Класс добавляет новый набор правил контекста к правилам базового языка (см. раздел 3.8). Одно из предназначений классов - обеспечение технологии инкапсуляции. Концептуально имеет смысл, чтобы все имена, объявленные внутри класса, обрабатывались внутри их собственного пространства имен, отлично от любых внешних имен, имен функций или имен прочих классов. Это создает потребность в операторе разрешения контекста.<br />
<strong>Оператор разрешения контекста : :</strong><br />
Оператор разрешения контекста — новый в C++, и является оператором с самым высоким приоритетом в языке. Он применяется в двух формах:<br />
::i         //одноместный оператор - ссылается на<br />
            //внешний контекст<br />
foo bar::i //двухместный оператор - ссылается на<br />
           //контекст класса<br />
Одноместная форма используется для раскрытия или обращения к имени, относящемуся ко внешнему контексту и скрытому локальным контекстом или контекстом класса.</p>
<pre><code>
int count = 0;            //внешняя переменная void how many(double w[], double x, int&#038; count)
{for (int i = 0; i < Ы; ++i)
  count += (w[i] == x);
  ++::count;            //следят за обращениями
}</code></pre>
<p>Двухместная форма используется для устранения неоднозначности имен, которые повторно используются внутри классов. Подобное его применение будет существенно при наследовании (см. главу 9).</p>
<pre><code>
class wicigets {public: void f();};
class gizmos {public: void f();};
void f() { / *что угодно* /)          //обычно внешняя f
void widgets::f(){/* что угодно*/}//f локальная для widgets
void gizmos::f(){/* что угодно*/} //f локальная для gizmos
</code></pre>
<p>Одна из точек зрения на использование оператора разрешения контекста состоит в том, чтобы рассматривать его как обеспечение маршрута к идентификатору. По умолчанию внешний маршрут отсутствует. Это аналогично тому, как работают структурные каталоги при именовании файлов.<br />
Внутри самого определения класса или при использовании его с селекторами члена для вызова функций-членов это свойство имени обычно не нужно, как видно в продолжении предыдущего примера:</p>
<pre<code>>
widgets w;
gizmos g;
g.f() ;
w.f();
g.gizmos::f(); //допустимо и избыточно
g.widgets::f(); //неверно: widgets::f() не может
                //действовать на gizmos
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.studcode.ru/archiv/klassy-operator-razresheniya-konteksta/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

