один прикладной вопрос по ООП

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 03.08.2012 22:31:19

Недавно мой коллега прислал мне код на C++, перевести который в FPC я так и не смог, как не бился...
Пример чисто виртуальный, никакой полезной нагрузки для практики не несёт вообще... но однако он основан на элементарщине в ООП... не понимаю почему почему FPC это не хавает... хотя Си такую вещь успешно исполняет, где у Паскаля - segmentation fail...
итак... из теории мы знаем, что методы живут отдельно от данных... в FPC почему-то не так...
вот исходный код на C++:
Код: Выделить всё
#include <iostream>
class Foo
{
public:
  void foo()
  {
    std::cout << "hello world" << std::endl;
  }
};

int main()
{
  Foo& foo = *(Foo*)NULL;
  foo.foo();
}


если кто ничерта не въезжает в подобные сишные приёмы...
с упрощением main() будет:

Код: Выделить всё
int main()
{
  Foo *foo = (Foo*)0;
  foo->foo();
}


здесь, эта вот хитрая конструкция "Foo *foo = (Foo*)0;" происходит от того, что в Си нет автоматического преобразования из нетипизированного указателя в типизированный, по сути это просто Foo * foo = 0, но писать надо так...

и вот это в C++ собирается и работает... написать аналог в FPC мне конечно удалось... и он собирается... но не работает...
это меня и озадачило... получается, что FPC не умеет отделять мух от котлет - методы от данных...

вот мой вариант перевода:

Код: Выделить всё
{$mode objfpc}
type
        Foo = class
                public
                        procedure foo;
        end;

procedure Foo.foo;
begin
        Writeln('test');
end;

var     
        _foo:^Foo;
begin
        _foo:=nil;
        _foo^.foo;
end.


вот, более аккуратный:
Код: Выделить всё
PFoo = ^TFoo;
TFoo = class
public
  procedure foo;
end;

procedure TFoo.foo;
begin
  writeln('Hello world!!!');
end;

var
  p: pointer;
  a: PFoo;

begin
  p := nil;
  a := p;
  a^.foo;
end.


почему это не работает??? где ошибка? с точки зрения основ ООП всё сделано верно!
Похоже на то, что паскаль в этот код другую семантику вкладывает. Непонятно почему
Разыменование pointer^.data понятно является косвенной адресацией относительно адреса pointer. Но pointer^.method() семантически означает другое. Он говорит о том, что нужно взять method() относящийся к типу указателя pointer и передать ему данные по указателю pointer. И почему это не работает - не знаю. Помогите, кто чем поможет)

ещё более точно:
Операция "точка" является перегруженной и имеет совсем разный смысл при обращении к полям и методам объекта класса. Именно в этом и суть данного примера в Си++ - отличная ловушка для экзаменуемых.
Начнём с того, как они понимают операцию "точка" в только что описанном контексте.
ВЕДЬ "type_pointer^.data" и "type_pointer^.method()" - это совсем разная семантика и совершенно разная работа с указателями внутри реализации!!!
Что нужно сделать чтобы получить адрес data? Надо выполнить косвенную адресацию относительно type_pointer
А что нужно сделать чтобы получить адрес метода method()?
!!! Для этого не нужно выполнять косвенную адресацию относительно type_pointer!!!
Для этого нужно знать тип указателя, чтобы определиться с пространством имен, где надо искать адрес функции method()!!!

почему это не работает?
Последний раз редактировалось Рождённый_в_СССР 03.08.2012 23:01:37, всего редактировалось 1 раз.
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение NTFS » 03.08.2012 22:58:33

foo() - это метод объекта, а не просто процедура.
Слава Паскалю, который не позволяет вызвать метод у несуществующего объекта.
Позор С++, который позволяет прострелить себе ногу двадцатью разными способами.
NTFS
постоялец
 
Сообщения: 388
Зарегистрирован: 05.11.2007 14:57:50
Откуда: Краснодар

Re: один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 03.08.2012 23:04:34

NTFS писал(а):foo() - это метод объекта, а не просто процедура.
Слава Паскалю, который не позволяет вызвать метод у несуществующего объекта.
Позор С++, который позволяет прострелить себе ногу двадцатью разными способами.


Но позвольте... Если метод объекта это просто объект с определенным namespace. И если я хочу вызвать элемент этого пространства имен, то почему мне не дают? Где логика???
Метод в общем виде не относится к объекту. Он относится к типу. Я согласен, что есть явные средства указать это - спецификатор static, но если метод не использует данные объекта, то никто не должен ему мешать работать.
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение NTFS » 03.08.2012 23:49:05

Логика в том, что такая возможность нужна, только чтобы "прострелить себе ногу". Хочется странного - ассемблер вам в помощь, там вообще можно все, хоть передавать управление на сегмент данных. А Паскаль - язык высокого уровня, и такого не позволяет, что прекрасно.
Практический пример. Объект не создан, но программист вызывает его метод, не взаимодействующий с данными - скажем, отправку сообщения наблюдателю о новом объекте. Что будет в правильном языке? Access Violation. Что будет в C++? Головная боль тому, кто станет искать мертвые души в протоколах программы.

И это, напишите, пожалуйста, какой компилятор. Что-то мне подсказывает, что приличные компиляторы С++ (вроде MinGW и Intel) ваш пример тоже не пропустят.

Добавлено спустя 1 минуту 10 секунд:
MinGW, подразумеваю gcc, конечно.

Добавлено спустя 3 минуты 1 секунду:
Только что проверил MinGW. Пример работает. И на этом языке написаны Linux, Windows и FireFox? Е..й стыд.
NTFS
постоялец
 
Сообщения: 388
Зарегистрирован: 05.11.2007 14:57:50
Откуда: Краснодар

Re: один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 04.08.2012 00:10:03

MinGW оказался приличным? круто... он же под винду... в моём коллективе признают только кроссплатформенные... т.к. мы в основном под Linux шпарим...
это собирается и работает под gcc/cpp (g++)...

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

Но, все-таки, можно сделать замечание, что метод не вызывается в общем случае относительно объекта. И если вы тут рассказываете про "правильную безопасность", то компилятор должен вылавливать для запрета именно случаи когда метод пытается разыменовать нулевой указатель. А тут мы никаких попыток разыменования указателя на данные не делаем.
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение Максим » 04.08.2012 00:16:55

Думаю, что class method спасёт отца русской демократии.
Аватара пользователя
Максим
энтузиаст
 
Сообщения: 598
Зарегистрирован: 27.07.2007 01:51:43
Откуда: Москва

Re: один прикладной вопрос по ООП

Сообщение NTFS » 04.08.2012 00:18:58

Стандарты - это набор рекомендаций, ИМХО.
А компиляторы и ЯП делаются людьми и для людей. И в этом плане Паскаль очень хорошо устроен. А С++, по вашему примеру, плохо. Потому что нет никакого смысла в вызове метода у несозданного объекта.

Добавлено спустя 2 минуты 4 секунды:
Максим:

Думаю, что class method спасёт отца русской демократии.

Да, в терминах C++ - это static. Но тут автор неудомевает, почему нельзя вызвать метод. Потому что нельзя быть уверенным, что он не будет использовать данные класса, пусть и опосредованно.
NTFS
постоялец
 
Сообщения: 388
Зарегистрирован: 05.11.2007 14:57:50
Откуда: Краснодар

Re: один прикладной вопрос по ООП

Сообщение Максим » 04.08.2012 00:29:22

Рождённый_в_СССР писал(а):MinGW оказался приличным? круто... он же под винду... в моём коллективе признают только кроссплатформенные...

Это отжиг недели. :mrgreen:

NTFS
Полностью согласен.

В любом случае, это старая тема для флейма. Всё сто раз уже было тёрто-перетёрто. Тот, кому нужен C++, должен скачать его компилятор, а не превращать в него Паскаль.
Аватара пользователя
Максим
энтузиаст
 
Сообщения: 598
Зарегистрирован: 27.07.2007 01:51:43
Откуда: Москва

Re: один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 04.08.2012 00:35:56

NTFS писал(а):Стандарты - это набор рекомендаций, ИМХО.
А компиляторы и ЯП делаются людьми и для людей. И в этом плане Паскаль очень хорошо устроен. А С++, по вашему примеру, плохо. Потому что нет никакого смысла в вызове метода у несозданного объекта.

Добавлено спустя 2 минуты 4 секунды:
Максим:

Думаю, что class method спасёт отца русской демократии.

Да, в терминах C++ - это static. Но тут автор неудомевает, почему нельзя вызвать метод. Потому что нельзя быть уверенным, что он не будет использовать данные класса, пусть и опосредованно.


стоп... вы не поняли развития событий...
если уж Паскаль не видет смысла в вызове метода у не созданного объекта, почему он это компилирует? Java, например, такого не соберёт и я это говорил...
а этот собирает и программа вываливает мне ошибку... уж либо сделайте чтобы FPC понимал то, что собирает, либо не собирайте то, что не можете правильно понять...
Если FPC "хороший компилятор", то почему программа падает в segmentation fault при том, что программа правильная. Со всех сторон ООП... да, возможно она даёт прострелить себе ногу, но компилятор мне вывалит ошибку, что я собираюсь прострелить себе ногу... а не тупо-молча всё это соберёт и предложит запустить, чтобы я прострелил себе ногу при том, что пистолет на предохранителе )))

Все правильно с точки зрения кода, компилятор не ругается, а при запуске - конец котенку

Добавлено спустя 11 минут 14 секунд:
Максим писал(а):Думаю, что class method спасёт отца русской демократии.

class method это не то... это аналог static в
в Си+++++ ( или сколько там плюсов?))) )
это
Код: Выделить всё
class A {
public:
  static void foo() {
    std::cout << "Hello world\n";
  }
}
A::foo()


тут вопрос о другом, т.к. static в контексте методов класса означает семантику именно "метод класса" а не "метод объекта класса". В Java аналогично, вплоть до static
static статиком, а наша задача к нему отношения не имеет, так как метод обозначенный таким образом меняет саму семантику вызова. Статическим методам не передается this (в паскале - self), так как они вообще к объекту отношения не просто не имеют, а им запрещено даже думать об объекте.

Ну если обратиться к теории ООП, то мы имеем примерно так. "Метод отличается от обычной функции/процедуры тем, что в сигнатуру его вызова включается неявный параметр - указатель на данные объекта. И все обращения к данным объекта в пространстве метода, на самом деле вычисляются как this->data, хоть и пишутся data"
В Си++ можно сделать так, например :
Код: Выделить всё
class A {
public:
  int data;
  void setData(int data);
};

void A:etData(int data) {
  this->data = data;
}

И так делается постоянно. Это считается правилом хорошего тона, чтобы не засорять пространство имен и максимально интуитивно понятным делать код.
В паскале, кстати, кажется такой фокус не сработает :(
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение svk12 » 04.08.2012 01:21:36

В Паскале это неявный параметр "Self".

Код: Выделить всё
.....
  Self.data:=data;
.....
svk12
постоялец
 
Сообщения: 408
Зарегистрирован: 09.06.2008 18:42:47

Re: один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 04.08.2012 01:29:44

svk12 писал(а):В Паскале это неявный параметр "Self".

Код: Выделить всё
.....
  Self.data:=data;
.....

то, что это self, я уже сказал, тема не об этом
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение Vapaamies » 04.08.2012 01:32:16

Рождённый_в_СССР писал(а):почему это не работает??? где ошибка?

В знании Паскаля, вестимо. Рабочий пример в Delphi 6:
Код: Выделить всё
type
  PObjectFoo = ^TObjectFoo;
  TObjectFoo = object
  public
    procedure Foo;
  end;

  TClassFoo = class
  public
    procedure Foo;
  end;

procedure TObjectFoo.Foo;
begin
  WriteLn('Hello object world!!!');
end;

procedure TClassFoo.Foo;
begin
  WriteLn('Hello class world!!!');
end;

begin
  PObjectFoo(nil).Foo;
  TClassFoo(nil).Foo;
end.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 292
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: один прикладной вопрос по ООП

Сообщение Рождённый_в_СССР » 04.08.2012 02:04:02

Vapaamies писал(а):
Рождённый_в_СССР писал(а):почему это не работает??? где ошибка?

В знании Паскаля, вестимо. Рабочий пример в Delphi 6:
Код: Выделить всё
type
  PObjectFoo = ^TObjectFoo;
  TObjectFoo = object
  public
    procedure Foo;
  end;

  TClassFoo = class
  public
    procedure Foo;
  end;

procedure TObjectFoo.Foo;
begin
  WriteLn('Hello object world!!!');
end;

procedure TClassFoo.Foo;
begin
  WriteLn('Hello class world!!!');
end;

begin
  PObjectFoo(nil).Foo;
  TClassFoo(nil).Foo;
end.


огромное спасибо!
fpc это в обоих режимах тоже исправно работает - что в режиме objfpc, что в режиме совместимости с Delphi...
Это именно, то, что надо было, чтобы утереть нос Сишникам... достойный ответ и именно в рамках ООП...
Внешне все нормально. И непонятно чем различается от варианта через явное создание переменной - объекта.
Действительно не догадался до этого...
хотя осадочек останется, почему тот очевидный вариант компилится в сверх-защищённом от ерунды Паскале и не работает...

ещё раз Вас благодарю...
Аватара пользователя
Рождённый_в_СССР
новенький
 
Сообщения: 65
Зарегистрирован: 08.08.2007 01:03:26
Откуда: Саратов

Re: один прикладной вопрос по ООП

Сообщение Максим » 04.08.2012 02:13:22

Код: Выделить всё
PFoo = ^TFoo;
TFoo = class

А почему это должно работать? Если бы не Vapaamies, я бы эту ошибку не заметил.

Признаться, я вообще удивлён, что обращение к методу несуществующего объекта допустимо.
Аватара пользователя
Максим
энтузиаст
 
Сообщения: 598
Зарегистрирован: 27.07.2007 01:51:43
Откуда: Москва

Re: один прикладной вопрос по ООП

Сообщение Vapaamies » 04.08.2012 02:25:51

Рождённый_в_СССР писал(а):почему тот очевидный вариант компилится в сверх-защищённом от ерунды Паскале и не работает...

Даже самый защищенный компилятор бессилен перед незнанием языка. AV происходил при разыменовании nil^.nil^: переменные типа класса уже являются ссылками, и, создавая ссылку на неё, получалась ссылка на ссылку. Исключение валилось раньше, до вызова метода не доходило.

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

А местные комментаторы аж целую теорию заговора развели...

NTFS писал(а):Потому что нет никакого смысла в вызове метода у несозданного объекта.

Смиялсо. Скажу вам по секрету: именно так работает TObject.Free.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 292
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

След.

Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 13

Рейтинг@Mail.ru