Помощь - Поиск - Пользователи - Календарь
Полная версия: Классы, наследственность
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
Account
Есть вот такая программа.

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>
#include <graphics.h>
#include <string.h>
#include <math.h>
void move(int i);
// è½áßß öêâôÉÇ
class Figure
{
protected:
int Color;
int CenterX;
int CenterY;
public:
Figure(int iCenterX, int iCenterY)
{
Color = RED;
CenterX = iCenterX;
CenterY = iCenterY;
}
void SetNewColor(int NewColor)
{
Hide();
Color=NewColor;
Show();
}
virtual void Show() = 0;
virtual void Hide()
{
int prev_col = Color;
Color=getbkcolor();
Show();
Color = prev_col;
}
void Move(int DeltaX, int DeltaY)
{
Hide();
CenterX+=DeltaX;
CenterY+=DeltaY;
Show();
}
virtual ~Figure() {};
};

class Butterfly: public Figure
{
private:
int Radius;
int RadiusX;
int RadiusY;
int Stangle;
int Endangle;
public:
Butterfly(int iCenterX, int iCenterY, int iStangle,int iEndangle,int iRadiusX,int iRadiusY,int iRadius) :
Figure(iCenterX, iCenterY)
{
Stangle=iStangle;
Endangle=iEndangle;
Radius=iRadius;
RadiusX = iRadiusX;
RadiusY = iRadiusY;
}
void Show();
};

class Hexe: public Figure
{
private:
int RRadius;
int rRadius;
public:
Hexe(int iCenterX, int iCenterY, int iRadius) :
Figure(iCenterX, iCenterY)
{
RRadius = iRadius;
rRadius = (sqrt(3)/2)*RRadius;
}
void Show();
};

class Combo:public Hexe,public Butterfly
{
public:
Combo(int iCenterX, int iCenterY, int iStangle,int iEndangle,int iRadiusX,int iRadiusY,int iRadius) :
Hexe( iCenterX, iCenterY, iRadius),Butterfly(iCenterX, iCenterY, iStangle,iEndangle, iRadiusX, iRadiusY,iRadius)
{}
void Show();
void Hide();
void Move(int x);
void SetNewColor(int colr);
~Combo(){};
};

void Hexe::Show()
{
int prev_color=getcolor();
setcolor(Color);
line(CenterX, CenterY+RRadius,CenterX-rRadius,CenterY+RRadius/2);
line(CenterX-rRadius,CenterY+RRadius/2,CenterX-rRadius,CenterY-RRadius/2);
line(CenterX-rRadius,CenterY-RRadius/2,CenterX,CenterY-RRadius);
line(CenterX,CenterY-RRadius,CenterX+rRadius,CenterY-RRadius/2);
line(CenterX+rRadius,CenterY-RRadius/2,CenterX+rRadius,CenterY+RRadius/2);
line(CenterX+rRadius,CenterY+RRadius/2,CenterX, CenterY+RRadius);
setcolor(prev_color);
}

void Butterfly::Show()
{
int prev_color=getcolor();
setcolor(Color);
circle(CenterX, CenterY-RadiusY, Radius/5);
ellipse(CenterX, CenterY+Radius/5, Stangle,Endangle,RadiusX,RadiusY);
line(CenterX-(sqrt(3)/2*Radius),CenterY-Radius/2,CenterX-20,CenterY);
line(CenterX-20,CenterY,CenterX-(sqrt(3)/2*Radius),CenterY+Radius/2);
line(CenterX-(sqrt(3)/2*Radius),CenterY-Radius/2,CenterX-(sqrt(3)/2*Radius),CenterY+Radius/2);
line(CenterX+(sqrt(3)/2*Radius),CenterY-Radius/2,CenterX+20,CenterY);
line(CenterX+20,CenterY,CenterX+(sqrt(3)/2*Radius),CenterY+Radius/2);
line(CenterX+(sqrt(3)/2*Radius),CenterY-Radius/2,CenterX+(sqrt(3)/2*Radius),CenterY+Radius/2);
setcolor(prev_color);
}

void Combo::Show()
{
Butterfly::Show();
Hexe::Show();
}

void Combo::Hide()
{
Butterfly::Hide();
Hexe::Hide();
}

void Combo::Move(int x)
{
Butterfly::Move(15*x,15*x);
Hexe::Move(15*x,15*x);
}

void Combo::SetNewColor(int colr)
{
Butterfly::SetNewColor(colr);
Hexe::SetNewColor(colr);
}

int main()
{ int n_menu;
while (1)
{
clrscr;
cout<<"1-Butterfly\n2-Hexe\n3-Combo\n4-Exit\n";
cin>>n_menu;
int gdriver = DETECT, gmode, errorcode;
initgraph(&gdriver, &gmode,
"../BGI");
errorcode = graphresult();
if (errorcode != grOk)
{
cerr<<"Graphics error";
cerr<<grapherrormsg(errorcode);
exit(1);
}
Butterfly F1(100,100,0,360,20,75,100);
Hexe F2(100,100,100);
Combo F3(100,100,0,360,20,75,100);
switch(n_menu)
{
case 1: cleardevice();F1.Show();break;
case 2: cleardevice();F2.Show();break;
case 3: cleardevice();F3.Show();break;
case 4: cout<<"Bay! Bay!\n";delay(750);return 0;
}
getch();
int maxcolor = getmaxcolor();
int direction = 1;
for (int color = 0; !kbhit(); color++)
{
if (color>maxcolor)
{color=0; direction*=-1;}

switch (n_menu)
{
case 1: F1.SetNewColor(color);F1.Move(10*direction,10*direction);break;
case 2: F2.SetNewColor(color);F2.Move(10*direction,10*direction);break;
case 3: F3.SetNewColor(color);F3.Move(direction);break;
}
delay(50);
}
closegraph();
}
}


По меню по очереди можно вывести бабочку, потом шестиугольник правильный, потом комбинированно бабочку в шестиугольнике. Бабочки и шестиугольник наследники от общего класса фигуры, а вот комбо от бабочки и шестиугольника. Не пойму вот следующее по идее класс Combo должен ведь унаследовать методы Move и SetNewColor, так как он наследник от Butterfly и Hex, которые в свою очередь наследуют данный метод от Figure. Но если выкинуть эти методы как сделано в выше представленном коде и напрямую обращаться например F3.SetNewColor(color);F3.Move(10*direction,10*direction); естественно компилятор материться. Так как правильно оформить наследственность и добраться до данных методов, не прописывая их в классе Combo и естественно не описывая так
void Combo::Move(int x)
{
Butterfly::Move(15*x,15*x);
Hexe::Move(15*x,15*x);
}

void Combo::SetNewColor(int colr)
{
Butterfly::SetNewColor(colr);
Hexe::SetNewColor(colr);
}


P.S. Как в спойлер код скрывать, а то как то не красиво длинный пост получается?
IUnknown
А теперь еще раз: что выкинуть? Где компилятор матерится? Что ты не хочешь описывать? Приведи тот код, который, как ты думаешь, должен компилироваться, но он не компилируется...

А то на данный момент вообще не понятно, что же тебе в приведенном коде не нравится...

Цитата
Как в спойлер код скрывать
Сделать [ hide="Заголовок спойлера" ]Текст спойлера[ /hide ]
Только теги - без пробелов...
Account
IUnknown, описание класса Combo

class Combo:public Hexe,public Butterfly
{
public:
Combo(int iCenterX, int iCenterY, int iStangle,int iEndangle,int iRadiusX,int iRadiusY,int iRadius) :
Hexe( iCenterX, iCenterY, iRadius),Butterfly(iCenterX, iCenterY, iStangle,iEndangle, iRadiusX, iRadiusY,iRadius)
{}
void Show();
void Hide();
void Move(int x);
void SetNewColor(int colr);
~Combo(){};
};


сами методы
void Combo::Show()
{
Butterfly::Show();
Hexe::Show();
}

void Combo::Hide()
{
Butterfly::Hide();
Hexe::Hide();
}

void Combo::Move(int x)
{
Butterfly::Move(15*x,15*x);
Hexe::Move(15*x,15*x);
}

void Combo::SetNewColor(int colr)
{
Butterfly::SetNewColor(colr);
Hexe::SetNewColor(colr);
}

преподаватель говорит зачем создавать методы void Hide(); void Move(int x); void SetNewColor(int colr); у данного класса, ведь они наследуются. Вот мне интересно как их использовать без создания а по наследству, ведь если их убрать из описания класса, а потом попробовать обратиться напрямую, как обращаешься например F1.Move или F2.Move (F1 -Butterfly, F2- Hex), вот так F3.Move тогда компилятор ругается.
Я так понимаю потому что данный класс не прямой потомок от класса Figure ?
IUnknown
Цитата
преподаватель говорит зачем создавать методы void Hide(); void Move(int x); void SetNewColor(int colr); у данного класса, ведь они наследуются. Вот мне интересно как их использовать без создания а по наследству
Преподаватель в своем уме? При множественном наследовании методов (да еще и виртуальных) с одинаковыми именами и с одной областью видимости (они все описываются в public-разделе, и наследование всегда общее, то есть все они видимы в потомках) возникает неоднозначность, которую компилятор не имеет права разрешать. Скажем:

#include <iostream>

class A
{
public:
virtual void f()
{
std::cout << "A::f()" << std::endl;
}
};

class B
{
public:
virtual void f()
{
std::cout << "A::f()" << std::endl;
}
};

class C : public A, public B
{
public:
C() : A(), B() { }
};

int main()
{
C obj;
obj.f(); // Какую из функций надо вызывать? Может одну, может обе. Тогда в каком порядке?
return 0;
}

Так что даже не выдумывай, без переопределения в классе Combo этой "прослойки" не обойдешься. Кстати, Липпман именно такой способ называет хорошим тоном программирования.

Но у тебя есть еще одна загвоздка.

Еще раз смотри: (Показать/Скрыть)


Оно тебе надо, чтоб базовый класс инициализировался 2 раза? Не надо.

Решение: (Показать/Скрыть)
Чувствуешь разницу?
Account
Цитата(IUnknown @ 4.07.2011 20:33) *

Преподаватель в своем уме?
.....
Чувствуешь разницу?

Может и не в своем, уже старенький.

По поводу разницы, почувствовал, спасибо, как всегда доходчиво.
Вот бы у нас так преподавали.
sheka
т.е. на всякий случай лучше всегда делать виртуальное наследование?
IUnknown
Цитата
т.е. на всякий случай лучше всегда делать виртуальное наследование?
На всякий случай надо знать об этом подводном камешке. И поступать так, как тебе нужно для решения задачи. Иногда (правда редко) нужно, чтобы при множественном наследовании базовый класс инициализировался при инициализации каждого потомка, иногда (чаще всего) не нужно. Поэтому, если ты будешь знать эту особенность - сможешь делать так, как нужно в твоем случае...
Account
Кстати, столкнулся еще с проблемой такой, что остаются следы после передвижения и именно от линий. Вот когда баловался выводил окружность, эллипс и двигал все нормально, как только появляются линии. при движении остаются следы, будто не стирается. Вот на примере выше, бабочка еще нормально, но шестиугольник и комбинированная фигура. при движении оставляет следы. В чем проблема и как от нее избавиться?
IUnknown
Вот кстати, без виртуального наследования следы однозначно будут оставаться при движении объекта типа Combo. Причем не только от линий, а и от эллипсов, то есть, объект будет просто напросто некорректно перерисовываться. Именно потому, что не надо второй раз инициализировать Figure... Рисоваться и стираться будут разные объекты. Как только ты сделаешь наследование виртуальным - этот глюк пропадет, объект Figure останется только один...

А насчет как избавиться от следов линий - не знаю, Wine запускать лениво, да и компилятор TurboC я уже стер... Теоретически - если ты уверен, что это линии "недоудаляются" - попробуй при стирании ставить бОльшую толщину линии (не NORM_WIDTH, который 1, как обычно, а THICK_WIDTH, который = 3). То есть, перед удалением установи через setlinestyle толстую линию, а потом, перед отрисовкой - опять обычную, узкую...
Account
IUnknown, добавил виртуальное наследование к бабочке и шестиугольнику, но компилятор ругается в конструкторе Combo на то что не найден конструктор для инициализации базового класса Figure (с английским у меня плохо, но что то в этом роде)
IUnknown
Цитата
но компилятор ругается в конструкторе Combo на то что не найден конструктор для инициализации базового класса Figure
smile.gif Я знал, что так и будет... Вас что, не учили виртуальному наследованию?

Сначала - как это происходит при наследовании обычном:
class base {
// ...
};

class A : public base {
// ... тут инициализируется базовый класс
};
class B : public base {
// ... и тут инициализируется базовый класс
};

class C : public A, public B {
// Здесь достаточно инициализировать прямых предков, но не ИХ предков
C() : A(), B() {}
// ...
};

, то есть, тебе было достаточно в списке инициализации написать только инициализацию тех классов, от которых ты наследовался, а уже они сами будут вызывать конструктор СВОИХ предков... Более того, тебе было запрещено писать в списке инициализации C() конструктор base, потому что ты от него напрямую не наследовался...

В случае с виртуальным базовым классом такой вариант не проходит. Класс C должен содержать одну копию base, поэтому надо инициализировать виртуальный базовый класс в наиболее удаленном от него потомке (чтоб потом не спрашивать себя, а откуда же был инициализирован базовый класс, из A или из B?). У тебя наиболее удаленный - не Hexe и не Butterfly, а Combo. Компилятор при виртуальном наследовании ждет инициализации не только Hexe + Butterfly, но и инициализации Figure. Если ты не записываешь его конструктор в список инициализации - будет вызван дефолтный конструктор (без параметров). У тебя и его нет, компилятор старый, не создает конструкторов самостоятельно. Надо добавить инициализацию ручками (все равно ты и в Hehe и в Butterfly одинаково вызываешь конструктор Figure):

class Combo : public Hexe, public Butterfly
{
public:
Combo(int iCenterX, int iCenterY, int iStangle, int iEndangle, int iRadiusX, int iRadiusY, int iRadius) :
Hexe(iCenterX, iCenterY, iRadius),
Butterfly(iCenterX, iCenterY, iStangle, iEndangle, iRadiusX, iRadiusY, iRadius),
Figure(iCenterX, iCenterY)
{}
// ...

, теперь компилятор знает, чем инициализировать базовый класс, и проблем не будет...
Account
Очередной раз спасибо за разъяснение. Учусь на заочном отделении, а там преподаватели не учат, а в лучшем случае если повезет могут дать консультацию.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.