type
TFigure = class
procedure Draw; virtual;
end;
TRectangle = class(TFigure)
procedure Draw; override;
end;
TEllipse = class(TFigure)
procedure Draw; override;
end;
проблема с перегрузкой функции
class Element
{
public:
virtual void save(FILE *f,const char *s) {}
void save(const char *s);
};
class Cascad:public Element
{
public:
void save(FILE *f,const char *s);
};
void Element::save(const char *s)
{
FILE *f;
f=fopen(s,"w");
save(f,s);
fclose(f);
}
void Cascad::save(FILE *f,const char *s)
{
......................
}
void main()
{
Cascad c;
c.save("2.txt");
}
При компиляции ошибка, пишет, что save - функция не одной переменной!
Но она ведь перегружена и передается по наследству в класс Cascad из класса Element.
Как исправить эту ошибку?
Сразу хочу вас предупредить, что сам я программирую на Delphi...
В Delphi при перегрузке ВИРТУАЛЬНЫХ и ДИНАМИЧЕСКИХ методов добавляется директива override. Вероятно в C++ то же есть что-то подобное.
Код:
Код:
Cascad c;
c.Element::save("2.txt");
c.Element::save("2.txt");
А не лучше организовать грамотную перегрузку метода...)))
Сразу хочу вас предупредить, что сам я программирую на Delphi...
В Delphi при перегрузке ВИРТУАЛЬНЫХ и ДИНАМИЧЕСКИХ методов добавляется директива override. Вероятно в C++ то же есть что-то подобное.
[/QUOTE]
А может, не лезть со своим уставом в чужой монастырь?
Читаем стандарт С++:
http://ftp.csci.csusb.edu/dick/c++std/cd2/over.html#over.dcl
Но не хочется писать c.Element::save(); Нужно вызывать c.save();
А если в классе Cascad описать метод save от одной переменной, в котором будет написана одна строчка
Element::save();
Тогда это согласуется со стандартом?
Here D::f(char*) hides B::f(int) rather than overloading it.
Конечно, тебе надо сделать так:
Код:
void Cascad::save(const char *s)
{
Element::save(s);
}
{
Element::save(s);
}
ну или назвать метод по-другому, что возможно даже правильнее с концептуальной точки зрения, т.к. методы выполняют, как я понял, все же разные действия.
Классов-потомков несколько, и каждый записывает свои данные в файл, притом метод save для каждого класса свой.
Я не знаю, не видел конкретной задачи, могу только предложить открывать файл в конструкторе, а закрывать в деструкторе Element(тогда при выходе из облости видимости объекта файл закроеться) и save зделать виртуальным. Но это совет я невидел задания и не знаю обстоятельств.?????
Первое правило программист, если тя не пытают и не просят зделать иначе - делай проше, потом сам рад будешь.
[/QUOTE]
К твоему сведению я вначале предупредил, что могу ошибаться...
Цитата:
Сразу хочу вас предупредить, что сам я программирую на Delphi...
И не говорил, что надо делать именно так, а не иначе, я сказал, что возможно такое решение...
Так что можно было и по культурней ответить...
А за каким писать тут, как это можно делать на Delphi. Никакой полезной информации это не несет. Просто так встрять в разговор и похвалиться знанием Delphi?
За таким, что захотелось человеку помочь....
Если бы Delphi хотелось мне похвалить, то я бы написал А вот в Delphi лучше из-за того что.......
А кроме того, если тут все такие ревнастные сторонники C++, что других не пускают, то и надо было перенасить обсуждение в соответствующие разделы форума...
Раздел между прочем называется Общие вопросы программирования
Если бы Delphi хотелось мне похвалить, то я бы написал А вот в Delphi лучше из-за того что.......
[/QUOTE]
Да не Delphi, а себя ))
Дело не в сторонниках. Есть просто хорошее правило. Не знаешь или не разбераешся в вопросе - не лезь с советами. =))
А вопрос был задан вполне конкретный. )
Дело не в сторонниках. Есть просто хорошее правило. Не знаешь или не разбераешся в вопросе - не лезь с советами. =))
А вопрос был задан вполне конкретный. )[/QUOTE]
Так и надо было об этом нормально сказать, а говорить, что ты такой да растакой, чего сюда вообще залез......
Кстати себя показать я не собирался, а просто хотел помочь.))
Кто перевый остановиться тот умнее будет)
Никто тут не ссорится. Просто выражаем предпочтения. =)
Кто перевый остановиться тот умнее будет)[/QUOTE]
А ни кто и не сорился))
)) А ты хоть ответ на свой вопрос увидел среди нашей маленькой перепалочки? )))
Цитата:
А ни кто и не сорился))
)) А ты хоть ответ на свой вопрос увидел среди нашей маленькой перепалочки? )))
)) А ты хоть ответ на свой вопрос увидел среди нашей маленькой перепалочки? )))
Если ты не заметил, то я дал один из многих вариантов ответа, а не задавал вопрос)
Код:
class Cascad: public Element
{
public:
using Element::save;
void save(FILE *f,const char *s);
};
{
public:
using Element::save;
void save(FILE *f,const char *s);
};
Не очень хорошее решение. Можно нарваться на не очень очевидные на первый взгляд грабли.
Стандарт ведь не просто так описывает эту ситуацию с хайдингом.
Интересно, кто-нибудь из присутствующих видит грабли такого решения? :)
Нет тут грабелей, вполне нормальное решение если хочешь перегрузить ф. базового класса, да у Страуструпа такая фишка описана).(p447 спец. изд. "The C++PL").
"Ты видишь суслика? Нет? А он есть!"
Я же дал целых две подсказки:
1) не очень очевидные на первый взгляд грабли ЕСТЬ,
2) стандарт не просто так описывает ситуацию с хайдингом.
Но если я прописываю явно ввести ф. в облость видимости, значит она мне нужна и должнен работать мех. перегрузки. => все нормально.
[/QUOTE]
Нет, не по этому.
Функции с одинаковыми именами - это обычная перегрузка, в ней нет ничего криминального.
[QUOTE='
Но если я прописываю явно ввести ф. в облость видимости, значит она мне нужна и должнен работать мех. перегрузки. => все нормально.
[/QUOTE]
Ещё раз повторяю грабли ЕСТЬ. Надо их найти. :)
Цитата:
Цитата: Сообщение отFrosty
Скрытие происходит потому, что ф. с одинаковыми именами могут выполнять различные действия по смыслу.
Нет, не по этому.
Скрытие происходит потому, что ф. с одинаковыми именами могут выполнять различные действия по смыслу.
Нет, не по этому.
Описаное мной - это причина(Перегрузка не пересекает границ класса.), если есть ещё говори). Не циклись на одном(есть анекдот про это). А то зациклился. Мир разнообразен, может быть куча причин, и это одна из них.
Описаное мной - это причина(Перегрузка не пересекает границ класса.), если есть ещё говори). Не циклись на одном(есть анекдот про это). А то зациклился. Мир разнообразен, может быть куча причин, и это одна из них.[/QUOTE]
Спасибо за психиатрические советы. :)
Я вижу грабли, но хочу послушать , что видят другие.
О том, что вижу я, скажу чуть позднее, чтоб другие не зациклились. Может, кто-то увидит ещё что-то.
1 причину уже привел.
2 причина на пример:
Код:
class B
{
void f(int);
}
class D:public B
{
public:
used B::f;
void f(int);
void f(char*);
}
g(D* pd)
{
pd->f(1)// Используеться D::f
}
{
void f(int);
}
class D:public B
{
public:
used B::f;
void f(int);
void f(char*);
}
g(D* pd)
{
pd->f(1)// Используеться D::f
}
Видно, что используеться D::f, если же интерфейс у класс больщой и D::f "затерялась", то по ошибке можно предположить, что используеться B::f
"затерялась" звучит по меньшей мере как-то непрофессионально.
Теперь о тех граблях, которые вижу я.
ООП предполагает, что изменение базового класса никак не должно отражаться на порядке вызовов дочернего класса. Думаю, никто с этим спорить не будет. Теперь рассмотрим такую картину:
Код:
#include <iostream>
using namespace std;
class A
{
public:
void func(int);
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
b.func("Hello!");
}
using namespace std;
class A
{
public:
void func(int);
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
b.func("Hello!");
}
В результате мы получаем на приветствие отзыв "Good day!".
Но потом базовый класс был немного изменен, поменялся его интерфейс:
Код:
#include <iostream>
using namespace std;
class A
{
public:
void func(int);
void func(const char*) { cout << "Good night!" << endl; }
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
b.func("Hello!");
}
using namespace std;
class A
{
public:
void func(int);
void func(const char*) { cout << "Good night!" << endl; }
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
b.func("Hello!");
}
и теперь на приветствие мы получаем "Good night!".
В результате изменения интерфейса базового класса в совокупности с неявным преобразованием наш метод void B::func(char *s) перестал вызываться!
Представляешь какие проблемы это может создать, например, при использовании сторонних библиотек?
Когда мы используем using, намерено предлагаем компилятору использовать перегрузку.
Ну да, изменение интерфейса класса очень часто приводит к нежелательным последствиям для пользователей этого класса. Именно поэтому хороший стиль программирования предполагает, что изменения класса не должны или должны как можно меньше влиять на существующий интерфейс. Вообще, лучше интерфейс задать раз и навсегда, если это возможно, и менять впоследствии только "внутренность" класса.
Так что эти грабли, в общем-то, вызваны не столько использованием using в производном классе, сколько нехорошим стилем программирования базового класса :). Сам же пишешь:
[QUOTE=Green]ООП предполагает, что изменение базового класса никак не должно отражаться на порядке вызовов дочернего класса[/QUOTE]
Расширение базового класса никак и не будет отражаться на порядке вызовов дочернего класса именно благодаря введению хайдинга (он именно поэтому и был введен), а использование using - это очередная "примочка" C++ несколько нарушающая общую концепцию, как например тот же potected, void* и т.п.
[QUOTE='
Когда мы используем using, намерено предлагаем компилятору использовать перегрузку.[/QUOTE]
И при этом теряем контроль над системой и получаем граблями...
Поэтому я и назвал это "не очень хорошим решением".
Пазор!
Так, что контроль никто не теряет.
Обычно using используеться когда мы явно хотим использовать механизм перегрузки. Например(что бы понятнее было)
B D
\ /
A
И в классах B и D в результате четкого и продуманного проекта нами введены одноименные методы и в A, мы хотим чтобы они использовались полностью и выбор происходил в пользу наиболее подходящего по параметрам.
Насчет protect, согласен, что он избыточен, но он не лишен он нужен бывает на стадии поддержки, сопровождения кода(Читай Страуструпа).
То же самое насчет void*, он нужен на низком уровне программирования, вдруг я захочу операционку наклепать.
В С++ нет нечего лишнего, просто нужно знать и правильно этим пользоваться.
Пазор!
[/QUOTE]
А ты запускать не пробовал?
Ты о моей репутации особо то не пекись. Я и сам как-нибудь выживу.
Говоришь не влияет, конкретный пункт стандарта в студию.
Типы char* и const char* - это совершенно разные типы, поэтому и разрешаться перегрузка для них будет по-разному.
Ок, не нравится char* и const char*, можешь заменить их в примере на float и double соотв-но. Собственно тип не имеет значение, имет значение неявное приведение.
[QUOTE='
Так, что контроль никто не теряет.
[/QUOTE]
Ну что сказать...
"Только бледнолицый брат наступает на одни и теже грабли дважды"
[QUOTE='
То же самое насчет void*, он нужен на низком уровне программирования, вдруг я захочу операционку наклепать.
[/QUOTE]
Язык С++ - не язык низкого уровня. А вдруг ты захочешь на PHP операционку наклепать? Мало ли что тебе в голову взбредет, язык то тут причем.
Приведи мне пример, где в С++ без void* не обойтись.
[QUOTE='
В С++ нет нечего лишнего, просто нужно знать и правильно этим пользоваться.[/QUOTE]
Просто не нужно обожествлять инструмент.
Тогда, ты неправильно написал, "непереносимо"(заранее говорю, это слово в ковычках), нужно было так-
Код:
#include <iostream>
using namespace std;
class A
{
public:
void func(int);
void func(const char*) { cout << "Good night!" << endl; }
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
const char* str = "Hello!";
b.func(str);// В этом случае будет уже "Good night!")
}
using namespace std;
class A
{
public:
void func(int);
void func(const char*) { cout << "Good night!" << endl; }
};
class B :public A
{
public:
using A::func;
void func(int);
void func(char*) { cout << "Good day!" << endl; }
};
void main()
{
B b;
const char* str = "Hello!";
b.func(str);// В этом случае будет уже "Good night!")
}
И никто не обожествляет инсрумент, просто не дураки писали, всему свое место, где-то ввели механизмы обхода существующих правил иногда это нужно. С++ гибок и в этом его мощь, по-этому например я всем советую начинать с Pas.
Цитата:
Язык С++ - не язык низкого уровня. А вдруг ты захочешь на PHP операционку наклепать? Мало ли что тебе в голову взбредет, язык то тут причем.
Приведи мне пример, где в С++ без void* не обойтись.
Приведи мне пример, где в С++ без void* не обойтись.
С++ - язык общего назначения, люди думали, что включать, подумали и решили, что на нем в нем нужны средства решения системных задач, вот и включили void*.
Тогда, ты неправильно написал, "непереносимо"(заранее говорю, это слово в ковычках)
[/QUOTE]
Не понял, о чем ты.
Я повторюсь, что тип не имеет значения, имеет значение неявное преобразование. Это может быть float и double, int и double, а уж с указателями на базовые и производные классы вообще огромное многообразие проблематичных вариантов в такой ситуации.
[QUOTE='
И никто не обожествляет инсрумент, просто не дураки писали, всему свое место, где-то ввели механизмы обхода существующих правил иногда это нужно. С++ гибок и в этом его мощь, по-этому например я всем советую начинать с Pas.
[/QUOTE]
Даже недураки создают неидеальные вещи, а С++ далек от идеала в т.ч. как ООП язык.
[QUOTE='
С++ - язык общего назначения, люди думали, что включать, подумали и решили, что на нем в нем нужны средства решения системных задач, вот и включили void*.[/QUOTE]
Неа... void* был включен лишь ради обратной совместимости.
И еще, по этому вопросу еще раз, попробую по чёче:
Если мы используем using, значит на нужно такое поведение, и мы хотим, что бы выбиралась наиболее подходящая ф. см. пред. сообщения, где про 2 базовых класса расписано.
Твой пример частично правилен. Но там - выбираеться наиболее подходящая ф., все так и задумано. Твои суслики - результат неправильного использования using. Все инструмент если неправильно использовать опасны.
Идеала нет но к нему нужно стремиться. И ООП идеал ли, сначал кажеться, да, а потом задумываешься, когда почитаешь. Ну даже если С++ не идеален, то внем все обосновано, и от болды не добавлялось, а ты говоришь
Цитата:
а использование using - это очередная "примочка" C++ несколько нарушающая общую концепцию, как например тот же potected, void* и т.п.
как будто они лишнии. Они нужны в определенных условиях. Да они нарушают, но иногда это необходимо.
[/QUOTE]
Извини, не вижу неточности.
An ordinary string literal has type "array of n const char" and static storage duration (_basic.stc_), where n is the size of the string as defined below, and is initialized with the given characters.
[QUOTE='
И еще, по этому вопросу еще раз, попробую по чёче:
Если мы используем using, значит на нужно такое поведение, и мы хотим, что бы выбиралась наиболее подходящая ф. см. пред. сообщения, где про 2 базовых класса расписано.
[/QUOTE]
Еще раз повторяю, такие конструкции приводят к потенциальным ошибкам. Если мы используем void*, то нам тоже нужно такое поведение, однако, это черевато опасностями, которых можно избежать не используя void*. Аналогично опасностей связанных с using можно избежать, если не использовать using, а это вполне возможно. Зачем лишний раз городить потенциально опасный код?
[QUOTE='
Твой пример частично правилен. Но там - выбираеться наиболее подходящая ф., все так и задумано. Твои суслики - результат неправильного использования using. Все инструмент если неправильно использовать опасны.
[/QUOTE]
Что же "неправильного" в моём использовании using?
Только то, что пример упрощен до максимально возможного, чтоб проблема была на поверхности.
[QUOTE='
Идеала нет но к нему нужно стремиться. И ООП идеал ли, сначал кажеться, да, а потом задумываешься, когда почитаешь. Ну даже если С++ не идеален, то внем все обосновано, и от болды не добавлялось, а ты говоришь
как будто они лишнии. Они нужны в определенных условиях. Да они нарушают, но иногда это необходимо.[/QUOTE]
Это необходимо крайне редко, возможно, даже вообще нет необходимости.
Этими средствами не нужно пользоваться, но они нужны иногда, поэтому и введены в язык.) Мы невсегда пишим программы с нуля иногда их приходиться сопровождать и на этом этапе втом числе возникают трудности, так как нужно модифицировать код, а он это непозволяет зделать "правильными" средствами, бываю ошибки на стадии проектирования, а может быть и так задумывалось(пример с 2-мя классами) .
Уфф... ну хоть в чем-то наши мнения сходятся... :)
Цитата:
ЦитатаFrosty:
Все инструмент если неправильно использовать опасны.
Все инструмент если неправильно использовать опасны.
Однако, лчно я не вижу необходимости использования using где-бы то ни было. Моё мнение, что вместо использования using правельнее и безопаснее явно определить как и что вызывать, а сделать это можно практически всегда, независимо от того поддерживается ли старый код или создается новый.
Прочитал этот полторастраничный флуд, и я так и не понял, в чем проблема с
Код:
Cascad: public Element
{
public:
using Element::save;
void save(FILE *f,const char *s);
};
{
public:
using Element::save;
void save(FILE *f,const char *s);
};
using Element::save; приводит к багу или нет? Ты привел какой-то довольно надуманный примерчик, но почему не привел пример с использованием класса Cascad, которая приводит к багу? Слабо? И желательно без дебильных изменений, типа если программист напется и изменит интерфейс таким образом тогда... То, что в другой программе кто-то неправильно использует using, как влияет на этот конкретный случай?