Конструкторы и операции



Конструкторы и операции

Важными моментами в жизни объектов являются те, когда они копируются или создаются на основе уже существующих. Реализация конструктора копирования объектов просто обязательна, если вы пользуетесь контейнером объектов. В случае отсутствия или некорректного тела конструктора контейнеры откажутся работать с объектами класса. Обычным приемом при этом является реализация в классе операции присвоения 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);

}



Содержание раздела