Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.03.15;
Скачать: [xml.tar.bz2];

Вниз

непереопределённые обстрактные методы   Найти похожие ветки 

 
XentaAbsenta ©   (2009-01-15 03:28) [0]

Скажите, почему делфя позволяет скомпилить прект если не все абстрактные методы переопределены, более того - она даже не матерится на это.


 
Германн ©   (2009-01-15 03:49) [1]


> XentaAbsenta ©   (15.01.09 03:28)
>
> Скажите, почему делфя позволяет скомпилить прект если не
> все абстрактные методы переопределены, более того - она
> даже не матерится на это.
>

А что в этом плохого?


 
Германн ©   (2009-01-15 03:53) [2]

В код исполняемой программы попадёт лишь то, что реально вызывается.


 
XentaAbsenta ©   (2009-01-15 04:05) [3]

плохого в этом то, что я периодически получаю EAbstractError, при том что вместо этого я бы просто не скомпилил проект


 
Котик Б   (2009-01-15 08:53) [4]

А вот интересно - если вы периодически получаете AV, обращаясь к нераспределённым объектам - тоже Делфай виновата ?


 
oxffff ©   (2009-01-15 09:07) [5]


> XentaAbsenta ©   (15.01.09 03:28)  
> Скажите, почему делфя позволяет скомпилить прект если не
> все абстрактные методы переопределены, более того - она
> даже не матерится на это.


Потому, что в момент компиляции полиморфного вызова тип объекта установить невозможно, из-за ссылочной семантики объекта.
Если бы этого небыло, тогда проблем было бы гораздо больше.
Например твой проект определяет контракты (абстрактные классы), подключает внешние реализации(DLL) и вызывает их через контракт базовый абстрактного класса. Если бы этого не было, то такой проект невозможно было бы сделать.


 
Anatoly Podgoretsky ©   (2009-01-15 09:07) [6]

> XentaAbsenta  (15.01.2009 3:28:00)  [0]

Потому что они могут быть недоступны на момент компиляции, например в BPL


 
KSergey ©   (2009-01-15 09:10) [7]

Я вот точно не знаю, но вероятно штука вот в чем.
В С++ нельзя создать экземпляр класса с заранее неизвестным типом, язык таков. Потому все на уровне компилятора можно отловить.

В дельфи - можно, через классовую переменную. Потому и нельзя на этапе компиляции в общем случае определить.
Впрочем, для явного выгова TAbstractClass.Create() вроде можно было бы и добавить compile-magic, но не сделали, видимо некритично.


 
oxffff ©   (2009-01-15 09:13) [8]


> KSergey ©   (15.01.09 09:10) [7]


Из-за разной семантики объекта

SomeClass* a;
a.SomeAbstractClass();

НО не

SomeClass b;
b.SomeAbstractClass();   <- попытка вызвать у известного типа.


 
KSergey ©   (2009-01-15 09:21) [9]

> Anatoly Podgoretsky ©   (15.01.09 09:07) [6]
> Потому что они могут быть недоступны на момент компиляции,  например в BPL

Я чета недогоняю этот нюанс: декларация же полностью доступна компилятору в любом случае, а в ней все есть.
Что я непонимаю?


 
KSergey ©   (2009-01-15 09:25) [10]

> oxffff ©   (15.01.09 09:13) [8]
> НО не
>
> SomeClass b;
> b.SomeAbstractClass();   <- попытка вызвать у известного типа.

Можно еще раз, для тупых, плиз
Я честно не понял семантики используего языка. Это типа С++? Тогда как понять a.SomeAbstractClass() ? Что это за метод? (предупреждаю, с реализацией фабрик на С++ не сталкивался, может от того непонимание)

Тип уже указан, SomeClass. И экземпляр уже сконструирован. А что за SomeAbstractClass тогда??


 
oxffff ©   (2009-01-15 09:26) [11]


> KSergey ©   (15.01.09 09:21) [9]


[8].

SomeClass* a;
a.SomeAbstractClass();

Как компилятор может догадаться, что вызов происходит будет только у абстрактого класса?


 
KSergey ©   (2009-01-15 09:31) [12]

> oxffff ©   (15.01.09 09:26) [11]
> SomeClass* a;
> a.SomeAbstractClass();

Я никак не могу понять этой записи: создан экземпляр указателя, и вдруг к нему идет обращение через точку, какой-то метод вызывается... Это какой-то smart pointer?


 
KSergey ©   (2009-01-15 09:32) [13]

хоты нет, это чистый указатель.... не понимаю


 
oxffff ©   (2009-01-15 09:33) [14]


> KSergey ©   (15.01.09 09:25) [10]
> Можно еще раз, для тупых, плиз


Ну не нужно скромничать. :)

Указателю на экземпляр можно присвоить экземпляр производных типов.

SomeClass* a;
a*.SomeAbstractMethod();

Поэтому установить нарушение можно в случае полного анализа полного потока программы. Что достаточно сложно.
А для подключаемых DLL с подклассами сделать невозможно.
Поэтому нарушение установить нельзя.

В сам экземпляр можно присвоить бинарно только экземпляр идентичного типа.

SomeClass b;
b.SomeAbstractMethod();  

Здесь b может быть только SomeClass.
Поэтому установить нарушение можно сразу.


 
oxffff ©   (2009-01-15 09:36) [15]


> oxffff ©   (15.01.09 09:33) [14]


У Dеlphi случай как раз SomeClass* a;


 
KSergey ©   (2009-01-15 09:42) [16]

> SomeClass* a;
> a*.SomeAbstractMethod();

Так не бывает. Пропущено собственно создание экземпляра объекта (new), как раз там это легко отлавливается в С++.


 
Alkid ©   (2009-01-15 09:46) [17]

Что-то вас в дебри понесло.
Вся штука в том, что Дельфи позволяет создавать экземпляры абстрактных классов, когда как C++, C#, Java - нет.


 
Anatoly Podgoretsky ©   (2009-01-15 09:47) [18]

> KSergey  (15.01.2009 9:21:09)  [9]

Откуда, да нет этого файла у программиста, он по заказу пишет, а BPL большой секрет.
Да и про полиморфизм не стоит забывать.


 
Anatoly Podgoretsky ©   (2009-01-15 09:49) [19]

Сообственно беспокоиться особо не о чем, будет ошибка в ран-тайм. Кроме этого еще много вещей, которые разрешаются в ран-тайм.


 
KSergey ©   (2009-01-15 09:50) [20]

> Alkid ©   (15.01.09 09:46) [17]
> Вся штука в том, что Дельфи позволяет создавать экземпляры
> абстрактных классов, когда как C++, C#, Java - нет.

Это понятно, хочется понять от чгео так сделано: просто так, или есть принципиально непреодолимые трудности сделать это на этапе компиляции в дельфи.


 
KSergey ©   (2009-01-15 09:52) [21]

> Anatoly Podgoretsky ©   (15.01.09 09:47) [18]
> Откуда, да нет этого файла у программиста, он по заказу  пишет, а BPL большой секрет.

С BPL не работал, но у меня большое сомнение вызывает возможность вызова несуществующего метода у класса, описанного в BPL. А раз так - значит и про абстрактность вроде можно было добавить битик.

Как мешает полиморфизм - я так же до сих пор не понимаю (кроме переменных типа ссылки на класс).


 
DrPass ©   (2009-01-15 10:03) [22]


> С BPL не работал, но у меня большое сомнение вызывает возможность
> вызова несуществующего метода у класса, описанного в BPL

"Рассказ не читал, но крайне не одобряю" (с) :)
Речь идет о том, что в Delphi реализовано связывание на уровне классов. Т.е. есть абстрактный класс, который описан в одном программном модуле, его морда выставлена в таблице импорта этого модуля. А его потомок с реализацией абстрактных методов может находиться совсем в другом программном модуле. Поскольку оба исполняемых файла компилируются раздельно, Delphi понятия не имеет, где находится (и находится ли вообще) реализация, и вполне справедливо оставляет эту проблему на совести программиста


 
oxffff ©   (2009-01-15 10:06) [23]


> KSergey ©   (15.01.09 09:42) [16]
> > SomeClass* a;
> > a*.SomeAbstractMethod();
>
> Так не бывает. Пропущено собственно создание экземпляра
> объекта (new), как раз там это легко отлавливается в С++.
>


Это не отлавливается. Если
функция принимает параметр SomeClass* a;
И делает вызов абстрактого метода.
Как она может узнать какого он кокретно класса. Никак особенно, если объект создается вне, например в DLL.


 
Григорьев Антон ©   (2009-01-15 10:11) [24]

Это связано с наличием в Delphi метаклассов и виртуальных конструкторов. Из-за этого не всегда на этапе компиляции можно решить, конструктор какого именно класса вызывается - с абстрактными методами, или его потомка, в котором они перекрыты. А раз 100%-ый отлов всё равно невозможен, разработчики Delphi решили не париться с этим вообще и отлавливать всё только в run-time.


 
Григорьев Антон ©   (2009-01-15 10:14) [25]


> oxffff ©   (15.01.09 10:06) [23]
> Это не отлавливается. Если
> функция принимает параметр SomeClass* a;
> И делает вызов абстрактого метода.
> Как она может узнать какого он кокретно класса. Никак особенно,
>  если объект создается вне, например в DLL.

Это здесь не при чём. Ошибка компилятора, по идее, должна возникать при попытке вызова конструктора для класса, содержащего абстрактные методы. Т.е. она должна возникать на строке a := TClassWithAbstractMethods.Create. Но, как я уже сказал, не всегда при компиляции возможно определить, конструктор какого класса будет вызван.


 
oxffff ©   (2009-01-15 10:16) [26]

> KSergey ©   (15.01.09 09:42) [16]
> > SomeClass* a;
> > a*.SomeAbstractMethod();
>
> Так не бывает. Пропущено собственно создание экземпляра
> объекта (new), как раз там это легко отлавливается в С++.
>

Метакласс здесь совершенно непричем.
Еще раз повторяю, если объект размещен на стеке,
то никакой полиморфизм не нужен, поскольку объект известного одного типа(его тип известен и он не может быть другим)
Поскольку на стеке объект размещен известного типа, и забиндить методы можно напрямую минуя VMT.

Если объект размещен в куче, соответственно обращение к нему через идет указатель. А указатель на тип может принимать и указатели на дочерние классы, в этом и заключается смысл полиморфизма. Принимать разные типы, и делать единый вызов.
Поэтому в точке вызова невозможно узнать тип, за исключением тех случаев
когда new перед ней. Однако если объект создается вне и передается указатель, то узнать тип(а соответственно корректность вызова) невозможно.
Конечно технически можно просто проверить, опросив RTTI объекта IsAbstractMethodImplemented. Однако здесь дополнительные расходы.
Нужен единый подход к вызову через указатель. Выбрали разрешено, без лишних проверок. В Delphi добавили просто информативные stub абстрактные методы.


 
oxffff ©   (2009-01-15 10:20) [27]


> Григорьев Антон ©   (15.01.09 10:11) [24]
> Это связано с наличием в Delphi метаклассов и виртуальных
> конструкторов. Из-за этого не всегда на этапе компиляции
> можно решить, конструктор какого именно класса вызывается
> - с абстрактными методами, или его потомка, в котором они
> перекрыты. А раз 100%-ый отлов всё равно невозможен, разработчики
> Delphi решили не париться с этим вообще и отлавливать всё
> только в run-time.


Согласен.


 
KSergey ©   (2009-01-15 10:22) [28]

> oxffff ©   (15.01.09 10:06) [23]
> И делает вызов абстрактого метода.
> Как она может узнать какого он кокретно класса.

А в функции и не надо отлавливать. Надо на создании отлавливать, т.к. это однозначно. Тогда и вызвать функцию с абстрактным классом ни у кого просто не получится.

А зачем все на эти DLL насели - мне вообще не понятно. Ни при чем оно тут совершенно.

> Григорьев Антон ©   (15.01.09 10:11) [24]

Спасибо, похоже в этом и есть единственная причина, я тоже так думаю.


 
oxffff ©   (2009-01-15 10:27) [29]


> Григорьев Антон ©   (15.01.09 10:14) [25]
>
> > oxffff ©   (15.01.09 10:06) [23]
> > Это не отлавливается. Если
> > функция принимает параметр SomeClass* a;
> > И делает вызов абстрактого метода.
> > Как она может узнать какого он кокретно класса. Никак
> особенно,
> >  если объект создается вне, например в DLL.
>
> Это здесь не при чём. Ошибка компилятора, по идее, должна
> возникать при попытке вызова конструктора для класса, содержащего
> абстрактные методы. Т.е. она должна возникать на строке
> a := TClassWithAbstractMethods.Create. Но, как я уже сказал,
>  не всегда при компиляции возможно определить, конструктор
> какого класса будет вызван.


С этим не согласен. Поэтому [26].
По причине, того разрешать или не разрешать создание экземпляров классов с абстракными методыми.
На этот вопрос ответа однозначного нет.
Это вопрос идеалогии.
И быть не может.

Рассмотрим класс А с некой фунциональностью и одним абстрактным методом. И его наследником B с перекрытым методом и расширенным других функионалом.
Зачем мне создавать B(дополнительные затраты), если мне нужна фунциональность A без абсрактного класса.
Одни скажут неправильная декомпозции, другие скажут проблема глубокой декомпозции и эффективности.
Это вопрос идеалогии.


 
oxffff ©   (2009-01-15 10:35) [30]


> Григорьев Антон ©   (15.01.09 10:14) [25]
>
> > oxffff ©   (15.01.09 10:06) [23]
> > Это не отлавливается. Если
> > функция принимает параметр SomeClass* a;
> > И делает вызов абстрактого метода.
> > Как она может узнать какого он кокретно класса. Никак
> особенно,
> >  если объект создается вне, например в DLL.
>
> Это здесь не при чём. Ошибка компилятора, по идее, должна
> возникать при попытке вызова конструктора для класса, содержащего
> абстрактные методы. Т.е. она должна возникать на строке
> a := TClassWithAbstractMethods.Create. Но, как я уже сказал,
>  не всегда при компиляции возможно определить, конструктор
> какого класса будет вызван.


Я тут подумал и решил, что все же не согласен.
Это совершенно не проблемма.
Достаточно в конструкторах абстрактных классов в Delphi вставить проверку.
if DL = 1 then raise Exception.create("AttemptTocreateAbstractClass").
Думаю до этого они догадаться точно смогли.
И создавайте хоть напрямую, хоть через метаклассы.

Думаете они тоже не дога


 
KSergey ©   (2009-01-15 10:52) [31]

чета все перемешано.
В C++ если ф-ция принимает указатель на абстрактный класс - беды с этим нет и даже вызывает абстрактный метод - беды в этом нет, т.к. все равно никто не сможет передать ей указатель на класс с абстрактным методом, т.к. сконструировать такой класс (хоть ан стеке, хоть на куче) невозможно.

Потому я и говорю, что DLL тут ваще ни при делах.


 
oxffff ©   (2009-01-15 10:55) [32]


> KSergey ©   (15.01.09 10:52) [31]
> чета все перемешано.
> В C++ если ф-ция принимает указатель на абстрактный класс
> - беды с этим нет и даже вызывает абстрактный метод - беды
> в этом нет, т.к. все равно никто не сможет передать ей указатель
> на класс с абстрактным методом, т.к. сконструировать такой
> класс (хоть ан стеке, хоть на куче) невозможно.
>
> Потому я и говорю, что DLL тут ваще ни при делах.


Это вопрос идеалогии разрешать или не разрешать создание классов с абстрактными методами.


 
Григорьев Антон ©   (2009-01-15 10:56) [33]


> oxffff ©   (15.01.09 10:27) [29]

Просто здесь прозвучало "как в C++, Java, C#" - вот я и рассказал, как это было бы в Delphi, если бы было как в этих языках.

> oxffff ©   (15.01.09 10:35) [30]

А смысл? Всё равно это проверка не во время компиляции.


 
oxffff ©   (2009-01-15 11:02) [34]

В Delphi решили перенести этот выбор на разработчика.

A class type declaration has the form

type
  className = class [abstract | sealed] (ancestorClass)
     memberList  
  end;

If a class is marked sealed, then it cannot be extended through inheritance. If a class is marked abstract, then it cannot be instantiated directly using the Create constructor. An entire class can be declared abstract even if it does not contain any abstract virtual methods. A class cannot be both abstract and sealed.


 
oxffff ©   (2009-01-15 11:02) [35]


> Григорьев Антон ©   (15.01.09 10:56) [33]
>
> > oxffff ©   (15.01.09 10:27) [29]
>
> Просто здесь прозвучало "как в C++, Java, C#" - вот я и
> рассказал, как это было бы в Delphi, если бы было как в
> этих языках.
>
> > oxffff ©   (15.01.09 10:35) [30]
>
> А смысл? Всё равно это проверка не во время компиляции.


Смысл простой это вопрос идеалогии. [34]


 
KSergey ©   (2009-01-15 11:22) [36]

> oxffff ©   (15.01.09 11:02) [34]

Прекольно, не ведал.


 
Alkid ©   (2009-01-15 12:26) [37]


> oxffff ©   (15.01.09 11:02) [35]
>
> Смысл простой это вопрос идеалогии. [34]

Я, вот, одного не понимаю - какой смысл разработчику создавать экземпляр класса с абстрактными методами? Мне бы очень хотелось бы узнать прецеденты, где какие-нибудь архитектурные задачи решались через создание экземпляров абстрактных классов. Пока же я твёрдо убеждён, что сама возможность так делать - зло.


 
Ega23 ©   (2009-01-15 12:35) [38]


> какой смысл разработчику создавать экземпляр класса с абстрактными
> методами?


1. От незнания (вопросов про "создание TStrings" за 5 лет я видел штук 50)
2. От не обращания внимания на warnings


 
XentaAbsenta ©   (2009-01-15 12:44) [39]

Это не вопрос идеалогии, это вопро здравого смысла. Если в классе присутствуют абстрактные методы, то инстанцировать класс нельзя, потому, что это ведёт к неминуемым ошибкам, тоже самое и с непереопределёнными абстрактными методами. По Моему Космически Невероятно Мега-Супер Скромному Мнению компилятор должен в таких случаях требовать от программиста посавить модификатор abstact в определении класса и ни при каких обстоятельствах не позволять инстанцировать класс даже через ссылку на класс, которая вполне может тоже может нести в себе аттрибуты класса.
А ошибки времени выполнения отлавливать и исправлять куда сложнее нежели ошибки компиляции.


 
oxffff ©   (2009-01-15 12:51) [40]


> Alkid ©   (15.01.09 12:26) [37]
>
> > oxffff ©   (15.01.09 11:02) [35]
> >
> > Смысл простой это вопрос идеалогии. [34]
>
> Я, вот, одного не понимаю - какой смысл разработчику создавать
> экземпляр класса с абстрактными методами? Мне бы очень хотелось
> бы узнать прецеденты, где какие-нибудь архитектурные задачи
> решались через создание экземпляров абстрактных классов.
>  Пока же я твёрдо убеждён, что сама возможность так делать
> - зло.


Существуют алтернативные мнения. [29]
Задача инструмента предоставить выбор.


 
Ega23 ©   (2009-01-15 12:55) [41]


> Если в классе присутствуют абстрактные методы, то инстанцировать
> класс нельзя, потому, что это ведёт к неминуемым ошибкам


unit 1.

TFoo = class (TObject)
public
 constructor Create; virtual;
 procedure DoSomething; virtual; abstract;
end;

TFooClass = class of TFoo;

TFooChild = class (TFoo)
public
 procedure DoSomething; override;
end;

implementation

constructor TFoo.Create;
begin
 inherited Create;
end;

procedure TFooChild.DoSomething;
public
 ... Чё-та делаем
end;

unit 2.

TMySuperPuperClass = class (....)
private
 FFoo : TFoo;
public
 constructor Create(FooClass : TFooClass);
 property Foo : TFoo read FFoo;
end;

constructor TMySuperPuperClass.Create(FooClass : TFooClass);
begin
 FFoo := FooClass.Create;
end;

unit 3

procedure Tform1.OnButton1Click(Sender : TObject)
begin
 wtih TMySuperPuperClass.Create(TFooChild) do
 begin  
   try
     Foo.DoSomething;
   finally
     Free;
   end;
 end;
end;



 
Игорь Шевченко ©   (2009-01-15 12:57) [42]

XentaAbsenta ©   (15.01.09 12:44) [39]

Выброси свой здравый смысл - конкретный наследник абстрактного класса может находиться в BPL и конструкция может выглядеть так:

type
 TFoo = class
 protected
    procedure Bar; abstract;
 end;

 TFooClass = class of TFoo;

.....
var
 FooClass: TFooClass;
 Foo: TFoo;
...
 FooClass := GetFooClassFromExternalSource;
 
 Foo := FooClass.Create;
...


Мне было бы крайне любопытно узнать, как при компиляции данного кода  проверить, является ли инстанцируемый класс абстрактным или содержит непереопределенные абстрактные методы


 
tesseract ©   (2009-01-15 12:58) [43]


> Это не вопрос идеалогии, это вопро здравого смысла. Если
> в классе присутствуют абстрактные методы, то инстанцировать
> класс нельзя, потому, что это ведёт к неминуемым ошибкам,
>


При наличии остуствия факт имеет место быть! Например  виртуальные методы и все события - суть абстракция. Их при создании класса может и не быть.


 
KSergey ©   (2009-01-15 13:05) [44]

> XentaAbsenta ©   (15.01.09 12:44) [39]
> компилятор ... ни при
> каких обстоятельствах не позволять инстанцировать класс даже через ссылку на класс

Штука в том, что компилятор дельфи в принципе не може этого знать в случае ссылки на класс. Ega23 ©   (15.01.09 12:55) [41] даже пример написал.

В синтаксисе С++ такого нет в приницпе, потому там все иначе.


 
tesseract ©   (2009-01-15 13:07) [45]


> Ega23 ©   (15.01.09 12:55) [41] даже пример написал.


Ага мне особенно  wtih  понравился - зачем на такой вопрос такой подробный код ? Тем более,  что есть ещё например AS.


 
KSergey ©   (2009-01-15 13:13) [46]

> Игорь Шевченко ©   (15.01.09 12:57) [42]
> класса может находиться в BPL и конструкция может выглядеть так:

Я никак не понимаю при чем тут BPL. В рамках одного проекта компилятор так же не в состоянии будет определить. Ну разве что прододумать логику поведения приложения на манер как определяются возможно неинициализируемые переменные, но и тут наверняка есть засада, когда это определить невозможно.


 
Alkid ©   (2009-01-15 13:15) [47]


> Игорь Шевченко ©   (15.01.09 12:57) [42]

Всё просто - попытка инстанциировать абстрактный класс - исключительная ситуация. Т.е. позволять создавать экземпляр нельзя никак. Если нельзя проверить этот факт в Compile-time, надо проверять его в Run-Time. Но экземпляр абстрактного класса не должен получаться ни при каких обстоятельствах.


> oxffff ©   (15.01.09 12:51) [40]

В [29] описана ситуация с неправильно спроектированной структурой классов и "хакерский" подход при работе с ней. Данная практика, ИМХО, порочна, поскольку является раскладыванием граблей на будущее, что бы на них можно было самому потом смачно наступить, когда ты уже забудешь про свой гениальный ход. Ну или для ближнего своего грабля получится, когда он на это напорется.

По сути ты предлагаешь создать объект, который не выполняет опубликованного контракта (интерфейса). Это небезопасный ход, который требует от тебя постоянно держать в голове тот факт, что для данного объекта действует только часть интерфейса.


 
tesseract ©   (2009-01-15 13:20) [48]


> Это небезопасный ход, который требует от тебя постоянно
> держать в голове тот факт, что для данного объекта действует
> только часть интерфейса.


Работу с VMT уже считают хакерским методом, а было  время когда других способов и не было.  То же  OLE  на позднем связывании и базируеться. В  Objective-C   или скриптовых все классы вообще тотальная абстракция - но работает же.


 
Ega23 ©   (2009-01-15 13:28) [49]


> Ага мне особенно  wtih  понравился - зачем на такой вопрос
> такой подробный код ? Тем более,  что есть ещё например
> AS.


А чё не так? И as при чём?


 
tesseract ©   (2009-01-15 13:30) [50]


> А чё не так? И as при чём?


По документации он безопастнее.


 
Ega23 ©   (2009-01-15 13:32) [51]


> По документации он безопастнее.


безопаснее ЧЕГО?


 
icWasya ©   (2009-01-15 13:37) [52]

А вот такое имеет право на существование ?


unit Classes;
...
TStream = class(TObject)
 private
 ...
 public
   function Read(var Buffer; Count: Longint): Longint; virtual; abstract;
   function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
   function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;

и
TPointerStream = Class(TStream)
...
piblic
   constructor Create(aP:Pointer;aSize:LongInt);
   function Read(var Buffer; Count: Longint): Longint; override;
   function Seek(Offset: Longint; Origin: Word): Longint; override;
end;

причём я точно знаю, что Write никогда не будет использоваться.


 
Alkid ©   (2009-01-15 13:44) [53]


> tesseract ©   (15.01.09 13:20) [48]
> Работу с VMT уже считают хакерским методом, а было  время
> когда других способов и не было.

А вот отсюда поподробнее пожалуйста. Впервые слышу, что работа с VMT уже считается хакерским методом :) Что ты подразумеваешь по работой с VMT?


> То же  OLE  на позднем
> связывании и базируеться. В  Objective-C   или скриптовых
> все классы вообще тотальная абстракция - но работает же.

На позднем связывании вообще значительная часть полиморфизма работает. И VMT - это лишь один из вариантов механизма позднего связывания. Он эффективен по скорости выполнения, но зато сильно ограничен. Например, multiple dispatch на нём закачаешься делать.

Касаемо скриптовых языков - это от языка зависит. Да, есть "безклассовые" ОО-языки. Их ещё называют ОО с прототипированием. Но там есть свои остроумные способы разобраться с попыткой применить к объекту метод, который в нём (для него) не определён. К теме этого разговора это всё не имеет прямого отношения.


 
Ega23 ©   (2009-01-15 13:45) [54]


> причём я точно знаю, что Write никогда не будет использоваться.


ага, а также WriteBuffer и CopyFrom?


 
tesseract ©   (2009-01-15 13:45) [55]


> безопаснее ЧЕГО?


Безопаснее  прямого  typecasting,


> А вот такое имеет право на существование ?


Это абстрактный класс перекрываемый наследниками. Abstarct error  можно вызвать не напрягаясь, но дело не в этом.


 
Anatoly Podgoretsky ©   (2009-01-15 13:51) [56]

> Ega23  (15.01.2009 13:32:51)  [51]

Безопаснее приведения.


 
Mystic ©   (2009-01-15 13:54) [57]

> По сути ты предлагаешь создать объект, который не выполняет
> опубликованного контракта (интерфейса). Это небезопасный
> ход, который требует от тебя постоянно держать в голове
> тот факт, что для данного объекта действует только часть
> интерфейса.


Во-первых, объект выполняет опубликованный интерфейс полностью. Просто у абстрактных методов реализация по умолчанию кидать exception. Я эту реализацию просто не меняю.

Во-вторых, ход как ход. Такой подход может быть полезным в разработке и в unit-тестах. Например, я создаю наследника от некоторого класса. Разработку веду по частям, вначале отлаживаю одну группу методов. Соответственно зачем мне вначале делать реализации всех абстрактных методов и помещать в каждый из них вызов raise ENotImplemented.Create() ???

В-третьих, это исключение можно ловить и обрабатывать. Т. е. использовать как часть логики. Дернули метод, получили EAbstractError, значит этот метод эту фичу не поддерживает. Или, по другому, опросили, что класс поддерживает. Узнали, что сортировка не поддерживается. Если все-таки вызвали метод Sort, то получили сигнал об ошибке---EAbstractError.

Как по мне, надо просто поменять точку зрения. В Delphi абстрактный метод имеет реализацию, это возбуждение исключения EAbstractError. Считай, что это вариант ENotImplemented. И используй. А чистых виртуальных методов а-ля С++ в Delphi нет.


 
Ega23 ©   (2009-01-15 13:54) [58]


> Безопаснее приведения.


Я не понял, а где у меня приведение (явное)?


 
tesseract ©   (2009-01-15 13:57) [59]


> Я не понял, а где у меня приведение (явное)?


> wtih TMySuperPuperClass.Create(TFooChild) do


Прямо вот тут абсолютное приведение с моторчиком.


 
Alkid ©   (2009-01-15 14:02) [60]


> Mystic ©   (15.01.09 13:54) [57]

Я понимаю твою точку зрения, но она напрямую противоречит уже заложенной в Дельфи философии на явность всех декларций. Overload, override, reintroduce и т.п. Т.е. программист явно говорит, что он тут хочет сказать, что бы не вышло случайной перегрузки или переопределения. То, что ты предлагаешь идёт вразрез с этой философией, по-этому я считаю это недосмотром с точки зрения разработчиков языка. Предлагаемый тобой подход в бОльшей степени подошёл бы C++, где компилятор без лишних деклараций делает выводы о перегрузке/переопределении.

Собственно говоря, всё это лишний раз подтверждает, что дисциплина поддержания идеологической целостности и чистоты в Дельфи хромает.


 
KSergey ©   (2009-01-15 14:07) [61]

> tesseract ©   (15.01.09 13:57) [59]
> > wtih TMySuperPuperClass.Create(TFooChild) do
> Прямо вот тут абсолютное приведение с моторчиком.

Здрасьте, приехали. Вчитайтесь в код внимательнее. Просто параметр передается в конструктор, никакого приведения нет.


 
XentaAbsenta ©   (2009-01-15 14:08) [62]

Игорь Шевченко ©   (15.01.09 12:57) [42]

> Мне было бы крайне любопытно узнать, как при компиляции
> данного кода  проверить, является ли инстанцируемый класс
> абстрактным или содержит непереопределенные абстрактные
> методы
>


Гороздо любопытнее то, как удалось так скомпилить bpl, так что в него не попало определение одного из базовых классов, или почему это позволил сделать компилятор.   Вот это уж точно здравому смыслу противоречит.
Скажите, уважаемый а что будет если я изменю код предка до неузнаваемости т.е.: в bpl находится класс T2, который наследуется от T1, я беру и уродую T1, сохраняя название класса, после чего компилирую(?!) и запускаю? А такая ситуация не редкость при переработке проекта, ногда базовые классы менять таки приходится.

Ega23 ©   (15.01.09 12:55) [41]
Приведённый код ничего не показывает. T = class of TMyAbstractClass;
T должно содержать признак абстрактности класса


 
Ega23 ©   (2009-01-15 14:12) [63]


> Прямо вот тут абсолютное приведение с моторчиком.


Макс, ты чё? Ты код мой внимательно посмотри.


 
Ega23 ©   (2009-01-15 14:13) [64]


> Приведённый код ничего не показывает. T = class of TMyAbstractClass;


И что? Я передаю в конструктор класс со всеми перекрытыми абстрактными методами.


 
tesseract ©   (2009-01-15 14:15) [65]


> Макс, ты чё? Ты код мой внимательно посмотри.


with  попутал. Обычно так и приводиться к знаменателю у меня 38 если что :-)


 
Ega23 ©   (2009-01-15 14:27) [66]


> у меня 38 если что :-)


сантиметров? Ходить не мешает?
:))))))))))))


 
Юрий Зотов ©   (2009-01-15 14:32) [67]

> XentaAbsenta ©   (15.01.09 12:44) [39]

> Если в классе присутствуют абстрактные методы, то инстанцировать
> класс нельзя, потому, что это ведёт к неминуемым ошибкам

Почему неминуемым? Если абстрактный метод реально нигде не вызывается, то и ошибок не будет.

> компилятор должен в таких случаях требовать от программиста посавить
> модификатор abstact в определении класса и ни при каких
> обстоятельствах не позволять инстанцировать класс

В силу предыдущего, компилятор должен выдать предупреждение (что он и делает), но не делать никаких запретов. Программист это предупреждение получает, но решает сам. Ему виднее, что и как он использует.

> даже через ссылку на класс, которая вполне может тоже может нести в
> себе аттрибуты класса.

Игорь уже привел пример, когда на этапе компиляции определить реальный класс невозможно.

> А ошибки времени выполнения отлавливать и исправлять куда сложнее
> нежели ошибки компиляции.

Вот с этим никто не спорит. Но если Вы включите показ варнингов и приучите себя программировать так, чтобы не было не только ошибок, но и даже хинтов (то есть, чтобы окошко сообщений компилятора всегда оставалось пустым), то как раз на этапе компиляции все плюшки и выловите.


 
Mystic ©   (2009-01-15 14:34) [68]


> напрямую противоречит уже заложенной в Дельфи философии
> на явность всех декларций. Overload, override, reintroduce
> и т.п.


А почему ты ничего не говоришь о safecall, inline? Одни модификаторы для явных деклараций, другие заставляют компилятор выполнять некоторые дополнительные действия... И я бы не говорил о какой-то идеологической целостности Delphi. Идеологическая целостность может быть у С++, где есть специальный комитет, который рассматривает предложения и говорит: эту фичу мы вносить не будем, потому что она противоречит идеологической целостности. Delphi, как мне кажется, больше формировался под влиянием практических потребностей. Насколько я помню, абстрактные методы были введены еще в Turbo Pascal. В те времена больше стояла проблема производительности, поэтому мне представляется вполне логичным, что создание объекта не перегружается дополнительными операциями. Delphi 1 изначально был под Win16, проблема производительности и там сохранялась. Более того, даже стала более актуальной ввиду того, что даже небольшие классы стали создаваться в куче. С другой стороны сами абстрактные методы достаточно редко используются в той же самой VCL. Вот и все :)


 
Alkid ©   (2009-01-15 14:38) [69]


> Юрий Зотов ©   (15.01.09 14:32) [67]
> Почему неминуемым? Если абстрактный метод реально нигде
> не вызывается, то и ошибок не будет.

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


 
Anatoly Podgoretsky ©   (2009-01-15 14:40) [70]


> Ega23 ©   (15.01.09 13:54) [58]

А я не знаю, о каком AS вы говорите, в [41] нет ни оператора AS , ни свойства AS , ни приведения.

Я отвею на вопрос, чем AS безопаснее.


 
Ega23 ©   (2009-01-15 14:47) [71]


> Я отвею на вопрос, чем AS безопаснее.


Не, я знаю что такое as. Что сначала проверка на is, а потом уже typecast. Я Макса не понял.


 
Mystic ©   (2009-01-15 14:51) [72]

> Alkid ©   (15.01.09 14:38) [69]

Ой, в реальном приложении таких мест столько... И абстрактные методы занимают столь малую часть от их числа...


 
icWasya ©   (2009-01-15 14:51) [73]

2 Ega23 ©   (15.01.09 13:45) [54]

>> причём я точно знаю, что Write никогда не будет использоваться.
>ага, а также WriteBuffer и CopyFrom?
ну естественно


 
XentaAbsenta ©   (2009-01-15 14:51) [74]

Григорьев Антон ©   (15.01.09 10:11) [24]


> Это связано с наличием в Delphi метаклассов и виртуальных
> конструкторов. Из-за этого не всегда на этапе компиляции
> можно решить, конструктор какого именно класса вызывается
> - с абстрактными методами, или его потомка, в котором они
> перекрыты. А раз 100%-ый отлов всё равно невозможен, разработчики
> Delphi решили не париться с этим вообще и отлавливать всё
> только в run-time.


T0 : abstract class
T1 : abstract class
T2 : class
----------
T0 содержит виртуальный конструктор Create(var tt : anytype), T1 тоже содержит, а T2 не содержит. В этом случае возможно инстанцирование T1, что недопустимо (напр. через ссылку на класс).
Выходом может быть ошибка компиляции класса T2 c требованием переопределить к виртуальный конструктор Create(var tt : anytype).
Что в купе с прямым запретом к инстанцированию абстрактных классов, приведёт к принципиальной невозможности их инстанцирования.

------------------
насчёт идеалогии - на кой ляд тогда нужно слово абстракт, для красоты?


 
Anatoly Podgoretsky ©   (2009-01-15 14:54) [75]

> Ega23  (15.01.2009 14:47:11)  [71]

А я подумал, что ко мне претензии.


 
Ega23 ©   (2009-01-15 14:55) [76]


> насчёт идеалогии - на кой ляд тогда нужно слово абстракт,
>  для красоты?


О! Это уже другой вопрос. Поройся в архиве, я весной эту тему поднимал. Достаточно жаркая дискуссия была. Как в виртуале, так и потом в реале.


 
XentaAbsenta ©   (2009-01-15 14:56) [77]

74
а это уже приведёт к принципиальной невозможности получить EAbstractError


 
Юрий Зотов ©   (2009-01-15 14:58) [78]

> Alkid ©   (15.01.09 14:38) [69]

Благодарю за пояснение, но оно лишнее - я достаточно давно работаю в командах, так что это (и не только это) эвристическое правило знаю даже слишком хорошо (увы).

Но как раз на такой случай варнинги и существуют. И этого достаточно.


 
Юрий Зотов ©   (2009-01-15 15:11) [79]

> XentaAbsenta ©   (15.01.09 14:51) [74]

> В этом случае возможно инстанцирование T1, что недопустимо (напр.
> через ссылку на класс). Выходом может быть ошибка компиляции класса
> T2 c требованием переопределить к виртуальный конструктор Create
> Что в купе с прямым запретом к инстанцированию абстрактных классов,
> приведёт к принципиальной невозможности их инстанцирования.

Даже если в T2 перекрыть конструктор, создать экземпляр T1 через ссылку на класс все равно будет можно. И на этапе компиляции будет невозможно отследить, экземпляр какого именно класса создается.

> на кой ляд тогда нужно слово абстракт, для красоты?

На тот ляд оно нужно, чтобы в НЕабстрактных методах абстрактного класса было можно вызывать его же абстрактные методы.

TFigure = class
private
 FSize: ...
 procedure SetSize(...);  
public
 procedure Draw; virtual; abstract;
 property Size: ... read FSize write SetSize;
end;

TCircle = class(TFigure)
public
 procedure Draw; override;
end;

procedure TFigure.SetSize(...);  
begin
 ...
 Draw; // Вот зачем нужен абстрактный метод
end;

procedure TCircle.Draw;
begin
 ... // реальная отрисовка
end;


 
oxffff ©   (2009-01-15 15:23) [80]


> Alkid ©   (15.01.09 13:15) [47]
>
> > oxffff ©   (15.01.09 12:51) [40]
>
> В [29] описана ситуация с неправильно спроектированной структурой
> классов и "хакерский" подход при работе с ней. Данная практика,
>  ИМХО, порочна, поскольку является раскладыванием граблей
> на будущее, что бы на них можно было самому потом смачно
> наступить, когда ты уже забудешь про свой гениальный ход.
>  Ну или для ближнего своего грабля получится, когда он на
> это напорется.
>
> По сути ты предлагаешь создать объект, который не выполняет
> опубликованного контракта (интерфейса). Это небезопасный
> ход, который требует от тебя постоянно держать в голове
> тот факт, что для данного объекта действует только часть
> интерфейса.


Я же говорю есть разные точки зрения. Жестокая декомпозиция приводит к потере эффективности. Поэтому нужна середина между гибкостью и эффективностью кода, размера приложения.
Мне ничего не стоит в этом классе вынести дополнительно контракт интерфейс(часть функциональности) и работать с ним.
Хотя я разделяю обе точки зрения, считаю что и так плохо и так плохо и в тоже время и так хорошо и так хорошо. :)
В вообщем в зависимости от условий делаю выбор. Самое главное, что есть средство которое это предоставляет.


 
oxffff ©   (2009-01-15 15:26) [81]


> Alkid ©   (15.01.09 14:02) [60]
>
> > Mystic ©   (15.01.09 13:54) [57]
>
> Я понимаю твою точку зрения, но она напрямую противоречит
> уже заложенной в Дельфи философии на явность всех декларций.
>  


oxffff ©   (15.01.09 11:02) [34]


 
oxffff ©   (2009-01-15 15:30) [82]


> Alkid ©   (15.01.09 14:02) [60]
>
> > Mystic ©   (15.01.09 13:54) [57]
>
> Я понимаю твою точку зрения, но она напрямую противоречит
> уже заложенной в Дельфи философии на явность всех декларций.
>  Overload, override, reintroduce и т.п. Т.е. программист
> явно говорит, что он тут хочет сказать, что бы не вышло
> случайной перегрузки или переопределения. То, что ты предлагаешь
> идёт вразрез с этой философией, по-этому я считаю это недосмотром
> с точки зрения разработчиков языка. Предлагаемый тобой подход
> в бОльшей степени подошёл бы C++, где компилятор без лишних
> деклараций делает выводы о перегрузке/переопределении.


Более того,, а почему бы нам например не поговорить о делегированию реализации через встроенный механизм.
Как компилятор может проверить, что у экземпляра будет инициализирован делегируемый объект.


>
> Собственно говоря, всё это лишний раз подтверждает, что
> дисциплина поддержания идеологической целостности и чистоты
> в Дельфи хромает


А откуда такой далекоидущий вывод?


 
XentaAbsenta ©   (2009-01-15 15:53) [83]


> Юрий Зотов ©   (15.01.09 15:11) [79]
>
> > XentaAbsenta ©   (15.01.09 14:51) [74]
>
> > В этом случае возможно инстанцирование T1, что недопустимо
> (напр.
> > через ссылку на класс). Выходом может быть ошибка компиляции
> класса
> > T2 c требованием переопределить к виртуальный конструктор
> Create
> > Что в купе с прямым запретом к инстанцированию абстрактных
> классов,
> > приведёт к принципиальной невозможности их инстанцирования.
>
>
> Даже если в T2 перекрыть конструктор, создать экземпляр
> T1 через ссылку на класс все равно будет можно. И на этапе
> компиляции будет невозможно отследить, экземпляр какого
> именно класса создается.


T1, T2, T3 наследники абстрактного T0
во всех их переопределён
constructor Create(var tt : anytype);virtual; abstract;

type
 TRef = class of T0;
var
  fff : TRef;
  hhhhh : T0;
begin
u := Random(4);
case (u) of
  1 : fff := T1;
  2 : fff := T2;
  else
    fff := T0;
end;
hhhhh := fff.Create(u);
end.


Действительно проблема имеет быть место. Действительно невозможно определить кого будет истанцировать, но выкинуть эксценшн в
hhhhh := fff.Create(u) можно!


 
Юрий Зотов ©   (2009-01-15 16:14) [84]

> XentaAbsenta ©   (15.01.09 15:53) [83]

> выкинуть эксценшн в hhhhh := fff.Create(u) можно!

Во-первых, это все равно уже будет на этапе исполнения.

Во-вторых, для этого нужно снабдить класс механизмом определения, все ли его абстрактные метды перекрыты. А зачем такое усложнение и дополнительные тормоза нужны, если это уже все равно run-time и при вызове неперекрытого абстрактного метода исключение и без того возникнет?

А если такого вызова реально нет, то и нет никакого криминала - значит и незачем зазря блокировать создание объекта.

Все логично, по-моему. При явном создании экземпляра абстрактного класса компилятор выбрасывает варнинг, а при неявном ошибка обнаруживается в run-time при вызове неперекрытого абстрактного метода.

Вполне разумное решение, IMHO.


 
Mystic ©   (2009-01-15 17:49) [85]


> Собственно говоря, всё это лишний раз подтверждает, что
> дисциплина поддержания идеологической целостности и чистоты
> в Дельфи хромает.


Я не демагог, поэтому вопросы идеологической ценности и чистоты меня мало интересуют. Мне надо, чтобы язык позволял мне эффективно решать поставленные задачи. Поэтому я себя несколько неуютно себя чувствую в языках типа Java: хочется поковырять отверткой в ухе, а из-за идеологической целостности сделать это нельзя.


 
Alkid ©   (2009-01-15 17:55) [86]


> oxffff ©   (15.01.09 15:30) [82]
> А откуда такой далекоидущий вывод?

Из наблюдений за языком.


> Mystic ©   (15.01.09 17:49) [85]
> Я не демагог, поэтому вопросы идеологической ценности и
> чистоты меня мало интересуют. Мне надо, чтобы язык позволял
> мне эффективно решать поставленные задачи. Поэтому я себя
> несколько неуютно себя чувствую в языках типа Java: хочется
> поковырять отверткой в ухе, а из-за идеологической целостности
> сделать это нельзя.

Значит ты апологет "хакерского" подхода к программированию. Такой подход тоже имеет право на существование, но я считаю его неправильным.


 
oxffff ©   (2009-01-15 18:38) [87]


> Alkid ©   (15.01.09 17:55) [86]
>
> > oxffff ©   (15.01.09 15:30) [82]
> > А откуда такой далекоидущий вывод?
>
> Из наблюдений за языком.


Хотелось услышать конкретные примеры нарушение идеалогической целостности Delphi?
В студию так сказать?


 
Медвежонок Пятачок ©   (2009-01-15 18:43) [88]

вся маета от привычки считать возбужденный эксепшен ошибкой.


 
oxffff ©   (2009-01-15 18:51) [89]


> Alkid ©   (15.01.09 17:55) [86]
> > Mystic ©   (15.01.09 17:49) [85]
...........
>
> Значит ты апологет "хакерского" подхода к программированию.
>  


Что это за подход к программированию?
Есть ситуации, когда прямое соблюдение идеалогии делает невозможным производить вполне безобидные операции. И проведение "прямых" манипуляций это вынужденная мера. Развитие концепции языка,
его type theory находится увы не в наших руках.
Поэтому хакерский код - это совсем из другой области.


 
Mystic ©   (2009-01-15 19:47) [90]


> Значит ты апологет "хакерского" подхода к программированию.
>  Такой подход тоже имеет право на существование, но я считаю
> его неправильным.


Хакерский стиль у меня больше ассоциируется с отсутствием всяких правил. Я сторонник здравого смысла и конкретного подхода к задаче и к каждому проекту. За все время программирования на Delphi я не могу припомнить ни одного случая, чтобы эта реализация абстрактного метода приводила к труднонаходимой ошибке. В случае блока try..except end; без очень веских на то причин, я готов бить канделябром по голове. Но если в системе создается абстрактный класс, то это не самое худшее, что может быть.

Описанная ситуация, что надо помнить о том, что вызов абстрактного метода ведет к исключению ничем не отличается от той, что при обращении к непроинициализированному полю может возникнуть AV, или если не установлено соединение с базой, то выполнение SQL-запроса приведет к исключению, а также если сокет закрыт, то посылать в него что-то бесполезно. И т. д. и т. п.


 
oxffff ©   (2009-01-15 20:45) [91]

>Mystic ©   (15.01.09 19:47) [90]

Полностью поддерживаю.


>  Но если в системе создается абстрактный класс, то это не
> самое худшее, что может быть.


Более того не составляет особого труда написать процедуру, которая будет при создании объекта проверять наличие абстрактных методов и информировать у удобном для программиста виде run time.


 
oxffff ©   (2009-01-15 20:47) [92]


> oxffff ©   (15.01.09 20:45) [91]


Естественно речь идет об универсальной процедуре,
которые я просто обожаю.


 
XentaAbsenta ©   (2009-01-16 01:01) [93]

хакерский подход к программированию не должен сущестовать как класс. Вырубить на корню, а несогласных кадилом по чайнику.
-------------
Он порождает демонов сложности и разрушает целостность проектов. Если хотите - открывайте тему - обосную.


 
oxffff ©   (2009-01-16 09:14) [94]


> XentaAbsenta ©   (16.01.09 01:01) [93]


Где можно записаться в Вам на курсы?


 
pasha_golub ©   (2009-01-16 09:23) [95]

Abstract"ы я выбрал бы только за то, что ворнинги сыплет изрядно! (с) моё

Написал класс, объявил асбтрактных методов. Начинаем писать наследников, хренакс, ворнинг. Спасибо. Забыл. Дописываю.

А если без этого то как? Понятно, что можно, но вопрос удобства разве не важен?



Страницы: 1 2 3 вся ветка

Форум: "Прочее";
Текущий архив: 2009.03.15;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.78 MB
Время: 0.066 c
15-1231142074
Зязиков Мухамед
2009-01-05 10:54
2009.03.15
шареваре сделать


15-1231929479
boriskb
2009-01-14 13:37
2009.03.15
Подскажите выбор


15-1231221351
KilkennyCat
2009-01-06 08:55
2009.03.15
HTML в CodeGear


2-1232820382
AlexDan
2009-01-24 21:06
2009.03.15
Оформление PageControl..


2-1232696590
Юзер
2009-01-23 10:43
2009.03.15
Подскажите как ???





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский