Конструкторы
и операции
Важными моментами
в жизни объектов являются те, когда они копируются или создаются на основе уже
существующих. Реализация конструктора копирования объектов просто обязательна,
если вы пользуетесь контейнером объектов. В случае отсутствия или некорректного
тела конструктора контейнеры откажутся работать с объектами класса. Обычным
приемом при этом является реализация в классе операции присвоения operator=
() и последующее ее воспроизведение в конструкторе копирования. Обратите внимание
на тип возвращаемого значения операции присвоения. Это ссылка (CPolygon&)
на активный или стоящий в левой части операции присвоения — *this, объект класса:
CPolygoni
CPolygon:
:operator=(const CPolygonS poly){
//======
Копируем все данные
m_pDoc
= poly.m_pDoc;
m_nPenWidth
= poly.m_nPenWidth;
m_PenColor
= poly.m_PenColor;
m_BrushColor
= poly.m_BrushColor;
m_ptLT
= poly.m_ptLT;
m_ptRB
= poly.m_ptRB;
//=====
Освобождаем контейнер точек
if
(!m_Points.empty()) m_Points.clear();
//======
Копируем все точки. Возможно решение с помощью assign.
for
(OINT i=0; i<poly.m_Points.size();
m_Points.push_back(poly.m_Points[i]
)
//======
Возвращаем собственный объект
return
*this;
//======
Конструктор копирования пользуется уже
//======
существующей реализацией операции присвоения
CPolygon::CPolygon(
const
CPolygoni poly)
{
*this
= poly;
}
Довольно часто
во вновь создаваемых классах переопределяют операцию выбора с помощью угловых
скобок ( [ ] ). Смысл этой операции задает программист. Он часто бывает очевидным
для классов объектов, содержащих внутри себя контейнеры, как в нашем случае.
Так, если к полигону poly применить операцию выбора, например
CDPoint
pt = poly[i];
то он возвратит
свою i-ю точку, что, безусловно, имеет смысл. Если же операция [ ] возвращает
ссылку на i-ю точку, то становится возможным использовать ее и в левой
части операции = (присвоения). Например,
poly[i]
= CDPoint (2.5, -20.);
Отметим, что
в новом языке С#, который поддерживается Studio.Net 7.0, такой прием является
встроенным средством языка под названием indexer. С учетом сказанного введите
следующую реализацию операции [ ]:
CDPointS
CPolygon::
operator[](UINT i)
{
if
(0 <= i && i < m_Points.size ())
return
m_Points[i];
return
m_ptLT;
}
Функция Set
для установки обратного указателя может быть совмещена (overloaded) с одноименной
функцией, позволяющей изменять атрибуты изображения полигона:
//======
Установка обратного указателя
void
CPolygon::Set (CTreeDoc *p) { m_pDoc = p;
{
//======
Совмещенная версия для изменения атрибутов
void
CPolygon::Set (CTreeDoc *p, COLORREF bCl, COLORREF pCl, UINT pen)
{
m_pDoc
= p;
m_BrushColor=
bCl;
m_PenColor
= pCl;
m_nPenWidth
= pen;
}
Деструктор
класса должен освобождать память, занимаемую вложенным в объект контейнером
точек:
CPolygon::~CPolygon()
{
m_Points.clear()
;
}
Метод GetRect
получает на входе ссылки на две характерные точки прямоугольника, обрамляющего
весь полигон, вычисляет координаты этих точек и возвращает их с помощью механизма
передачи ссылкой:
void
CPolygon::GetRect(CDPointS
ptLT, CDPointi ptRB)
{
m_ptLT
= m_ptRB = CDPoint(0., 0 .) ;
//======
Если полигон содержит точки контура
UINT
n = ra_Points.size();
if
(n > 0)
{
//======
Пробег по всем его точкам
for
(UINT 1=0; i<n; i++)
{
//======
Поиск и запоминание экстремумов
double
х = m_Points[i].x,
у
= m_Points[i].у;
if
(x < m_ptLT.x) m_ptLT.x = x;
else
if (x
> m_ptRB.x)
m_ptRB.x
= m_Points[i].x; if (y > m_ptLT.y) ra_ptLT.y = y;
else
if (y < m_ptRB.y)
m_ptRB.y
= y;
}
}
//======
Возвращаем найденные координаты (ссылками)
ptLT
= m_ptLT; ptRB = m_ptRB;
}
Метод сериализации
данных полигона, приведенный ниже, мог бы быть более компактным, если бы для
хранения точек полигона мы воспользовались бы одним из шаблонов семейства классов
Collection библиотеки MFC. В эти классы уже встроена возможность сериализации.
Но у нас на вооружении шаблон классов vector из другой библиотеки STL, так как
он обладает рядом других привлекательных черт. За это приходится платить несколькими
лишними строками кода, в котором все точки контейнера либо помещаются в архив,
либо выбираются из него:
void
CPolygon: :Serialize
(CArchiveS ar) {
//======
Если идет запись в архив,
if
(ar. IsStoring() }
{
//===
то последовательно переносим туда все данные
m
« m_nPenWidth « m_PenColor « m_BrushColor « m_Points. size () « m_ptLT.x « m_ptLT.y
« m_ptRB.x « m_ptRB.y;
for
(UINT i=0; i <m_Points . size 0 ;
m
« m_Points [i] .x « m_Points [i] . y;
}
else
{
//===
При чтении из архива меняем направление обмена
UINT
size;
m
» m_nPenWidth » m_PenColor » m_BrushColor
»
size » m_ptLT.x » m_ptLT.y
»
m_ptRB.x » m_ptRB.y;
//======
Заново создаем контейнер точек полигона
m_Points
. clear ( ) ;
while
(size--)
{
double
x, y;
m
» x » y;
m_Points.
oush back (CDPoint (x, v) ) ;
}
}
}
Ниже приведена
функция рисования полигона в переданный ей в качестве параметра контекст устройства.
Второй параметр является флагом, который задает способ заливки полигона. В операциях
визуального редактирования, которое мы введем позже, полигон должен временно
терять свой цвет, для того чтобы не было мелькания при частых перерисовках.
Напомним, что
полигон хранит World-координаты всех своих точек в контейнере m_Points. Переход
к Page-координатам производится с помощью функции MapToLogPt, которую мы еще
должны разработать и поместить в класс документа. Двигаясь далее по коду функции
Draw, мы видим, как объект настраивает контекст устройства с помощью своих личных
атрибутов и изображает себя в этом контексте:
void
CPolygon::Draw
(CDC *pDC,
bool bContour)
{
//======
Размер контейнера World-координат точек
UINT
nPoints = m_Points.size();
if
(!nPoints)
return;
//======
Временный массив логических координат точек
CPoint
*pts = new CPoint[nPoints];
//======
Преобразование координат
for
(UINT i=0; KnPoints; i++)
pts[i]
= m_pDoc->MapToLogPt(m_Points[i]);
pDC->SaveDC();
CPen
pen (PS_SOLID,m_nPenWidth,m_PenColor);
pDC->SelectObject(Spen);
CBrush
brush (bContour ? GetSysColor(COLOR_WINDOW) : m_BrushColor);
pDC->SelectObject(ibrush);
//======
Полигон изображается в предварительно
//======
подготовленном контексте устройства
pDC->Polygon(pts,
nPoints);
//======
Освобождаем массив
delete
[] pts;
pDC->RestoreDC(-1);
}