Страниц: 1 [2] 3 4 ... 10
 11 
 : Ноября 06, 2018, 12:10:32 pm 
Автор Hed - Последний ответ от Hed
Привет всем!

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

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

 12 
 : Октября 21, 2018, 11:46:36 am 
Автор ia - Последний ответ от ia
Отчет о работе за 12 лет.

Написал шаблон программы-сервера.
Довел до законченного состояния.
А именно, добавил все, что надо, поправил все, что мог,
синхронизировал версии на С и С++, сопроводил примером.
И, главное, наконец-то, документировал. Да еще по ГОСТу.

Все!


P.S. Результат выложил.
Адрес прежний http://resmgr.narod.ru

 13 
 : Октября 20, 2018, 08:54:37 am 
Автор kim - Последний ответ от da-nie
Цитировать
метод должен принадлежать классу (не иметь дополнительных каких либо смещений при ссылке на метод)

Про смещения - это уже проблема компилятора, как он будет искать этот метод. Если я вставил дверь в дом, отныне ручка двери принадлежит дому точно так же, как и двери. Это уже пусть сам компилятор ищет, где там этот метод у кого лежит. И жаль, что он такого не умеет.

Цитировать
Но нет никакой возможности вызывать непосредственно этот метод без указания типа (смещения) конкретного базового класса.

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

Цитировать
Компилятор не сможет различить полную сигнатуру (а именно именную сигнатуру) к каким методам будет применен вызов (на уровне  CErrors::ChangeState - это неизвестно). Он затрудняется гарантировать точные ваши намерения и принимает очевидное решение - сгенерировать ошибку компиляции. Гарантировано он может обеспечить работу только для собственных методов принадлежащих к CErrors (в примере это метод SetError)

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

Цитировать
И наличие конструктора - тут не причем.

Нет-нет, тут другой вопрос был. Итак, у нас есть два модуля.
В первом в *.h объявлен класс CErrors и в *.cpp написано так:

//глобальная переменная
char name[]="123";
//конструктор
CErrors::CErrors(void)
{
 printf("%s\r\n",name);
}

Во втором в *.h объявлен класс CMain и в *.cpp написано так:
//глобальная переменная
CMain cMain;
//конструктор
CMain::CMain(void)
{
 printf("CMain!\r\n");
 CErrors *cErrors_Ptr=new CErrors();
 delete(cErrors_Ptr);
}

Модули разные, поэтому порядок инициализации между ними произвольный. Пусть CMain инициализируется первым. Вопрос был в том, будет к этому моменту инициализирована переменная name или нет. Если name задать как std::string, то точно гарантий нет. А вот если задать массивом, то получается, что да.

 14 
 : Октября 20, 2018, 03:31:59 am 
Автор kim - Последний ответ от lastcross
Цитировать
Тут проблема веселее, как мне кажется. Вообще, обычно принято делать поведение системы предсказуемым и ожидаемым.
А давайте посмотрим, что может значить запись :
Код:
typedef (CErrors::*set_function_ptr)(uint32_t,bool);
- это синоним set_function_ptr на указатель функции с сигнатурой (uint32_t,bool). При этом мы явно говорим, что метод должен принадлежать классу (не иметь дополнительных каких либо смещений при ссылке на метод). Вот ваш же пример - в нем добавлен не виртуальный метод doSome() в базовые классы. В мэйне вызывается конкретные методы объекта cErrors. Но нет никакой возможности вызывать непосредственно этот метод без указания типа (смещения) конкретного базового класса. Да, если методы не пересекаются по имени/сигнатуре - это очевидная ошибка компиляции отловить не получится. Но это только потому что мы явно вызываем методы и компилятор точно может определить наши намерения. В вашем случае - вы переводите все в рантайм. Компилятор не сможет различить полную сигнатуру (а именно именную сигнатуру) к каким методам будет применен вызов (на уровне  CErrors::ChangeState - это неизвестно). Он затрудняется гарантировать точные ваши намерения и принимает очевидное решение - сгенерировать ошибку компиляции. Гарантировано он может обеспечить работу только для собственных методов принадлежащих к CErrors (в примере это метод SetError)


Цитировать
Тем, что для объектов вызывается конструктор, а переменные просто инициализируются из секций исполняемого файла.
То есть,
char name[]="123"; - инициализируется из секции .data
CErrors cErrors; - инициализируется вызовом конструктора.

Меня интересовал вопрос, инициализируются ли классы строго после все простых типов. Судя по тому, что написано, да.
Нет, вы не совсем все верно поняли. В описании разделены - константная статическая инициализация и .. другие типы статической инициализации. И в вашем случае порядок инициализации будет в порядке их перечисления. "123" будет лежать где-то в .data как некий литерал, но сам name будет инициализироваться в любом случае в процессе выполнения и раньше  чем cErrors. И наличие конструктора - тут не причем.

 15 
 : Октября 19, 2018, 07:29:25 pm 
Автор kim - Последний ответ от da-nie
Цитировать
делайте что-ли упрощенные ссылки на уже компилируемый пример

А это мысль. Cool Я как-то про такую возможность не подумал.
Вот: http://coliru.stacked-crooked.com/a/5e8e0c4f76c7cb1a Но в этой системе такое даже не компилируется (вроде бы нигде не опечатался...). Smiley

Цитировать
Все это компилятору не нравится

Тут проблема веселее, как мне кажется. Вообще, обычно принято делать поведение системы предсказуемым и ожидаемым. В данном случае компилятор себя так не ведёт. Я вполне чётко ему обозначил, что я желаю унаследовать два класса (CErrorsACU и CErrorsAcs) и чтобы каждый из них мог вызывать определённую в их суперклассе (CErrorsBased) и переопределённую в их потомке (CErrors) функцию. Для этого я указал компилятору, что желаю иметь указатель на функцию потомка CErrors. Я ожидаю, что функция, на которую сделан указатель, пусть и определена в классе CErrorsAcs, но была унаследована CErrors и отныне она принадлежит именно ему. Вроде бы достаточно понятное и простое желание. Поэтому я никак не ожидаю от компилятора перенаправления вызова этой функции в класс CErrorsAcs с последующей потерей связи с его потомком CErrors. Всё-таки, какая мне разница, что за проблемы возникают при этом у компилятора?  Angry

Цитировать
Вообще на первый взгляд - вот так кот путать пользователя передачей обработчиков методов разного уровня наследования .. в данной реализации мне показалась сомнительной.

Да там всё просто. Был класс, в котором была куча функций типа "Включить ошибку устройства ..." и "А включена ли ошибка устройства ... ?". Эти функции записывали/считывали флаги большой структуры, которая через разделяемую память передавалась процессам имитации этих устройств. Но на этапе реализации оказалось, что этих функций будет пара сотен. Не, так неудобно. Поэтому я выделил устройства в классы с их функциями и хотел чтобы все те же их функции были просто унаследованы исходным классом. Дальше встал вопрос сделать триггерную функцию "если включено, то выключить, иначе включить". Я это сделал через указатели на функции этого класса. Тут-то компилятор и возопил. Smiley

Цитировать
объекты/переменные - чем отличаются в вашем контексте?

Тем, что для объектов вызывается конструктор, а переменные просто инициализируются из секций исполняемого файла.
То есть,
char name[]="123"; - инициализируется из секции .data
CErrors cErrors; - инициализируется вызовом конструктора.

Меня интересовал вопрос, инициализируются ли классы строго после все простых типов. Судя по тому, что написано, да.

 16 
 : Октября 19, 2018, 06:29:01 pm 
Автор kim - Последний ответ от lastcross
Честно говоря, ни черта не понял, что там написано. Undecided
А так, чисто логически, непонятно, почему я не могу создать указатель на унаследованную функцию и вызвав эту функцию не получить преобразование к унаследованному классу.

На самом деле там две проблемы (только из-за специфики кода сразу не заметно было что когда и где вызывается - делайте что-ли упрощенные ссылки на уже компилируемый пример в том-же Coliru).
Проблема номер 1:

 - Наследуемые классы от виртуальных классов из-за специфики "виртуальности" своих родителей вынуждены "объединять" данные родителей. А следовательно адреса по которым располагаются их данные зависят от двух (или возможно более в зависимости от иерархий) смещений - адреса объекта  к которому они принадлежат + адреса родителя к которому они относятся. По этому люди удивляются - как, так ? ведь я передаю адрес объекта - он ведь должен автоматически должен догадаться в поиске метода. Но компилятор совсем путается когда вы даете адресс объекта вызывая сигнатуру метода указанного с учетом смещения другого типа.  Ну как-то так, если я правильно понял объяснение. Сам же я - не уверен на 100%,  что именно по этой причине - но очень именно похоже для вашего случая что так

Проблема номер 2:

Как вы заметили позже дав кусок материала по "делегатам" - с виртуальными методами дело обстоит схожим способом. Они адресуются не просто смещением от адреса объекта - а еще и со смещением в зависимости от виртуальной таблицы ..

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

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

Цитировать
О, спасибо!
Если я правильно понял,что там написано, гарантируется инициализация переменных до объектов.

Я не понял вашего вопроса. Объекты/переменные - чем отличаются в вашем контексте? А лучше пример.

 17 
 : Октября 19, 2018, 08:16:04 am 
Автор kim - Последний ответ от da-nie
Нашёл занятное описание проблемы с указателями на функции классов:

Цитировать
Жуткие сведения об указателях на функции-члены

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

class SomeClass
{
  public:
    virtual void some_member_func(int k, char* p)
    {
      printf(“In SomeClass”);  
    };
}

class DerivedClass : public SomeClass
{
  public:
// Если разкомментировать следующую строку, то код в строке * будет выдавать ошибку
// virtual void void some_member_func(int k, char* p) {printf(“In DerivedClass”); };
};

int main()
{
  // Объявляем указатель на функцию-член класса SomeClass
  typedef void (SomeClass::*SomeClassMFP)(int, char *);
  SomeClassMFP my_memfunc_ptr;
my_memfunc_ptr = &DerivedClass::some_member_func; // ----- строка (*)
}

Довольно любопытно, &DerivedClass::some_member_func являтся указателем на функцию-член класса SomeClass. Это не член класса DerivedClass! Некоторые компиляторы ведут себя несколько иначе: например, Digital Mars C++ считает в данном случае, что &DerivedClass::some_member_func не определен. Но если DerivedClass переопределяет some_member_func, код не будет скомпилирован, т.к. &DerivedClass::some_member_func теперь становится указателем на функцию-член класса DerivedClass!

Приведение между указателями на функции-члены – крайне темная область. Во время стандартизации С++ было много дискуссий по поводу того, разрешено ли приводить указатели на функции-члены одного класса к указателям на функции-члены базового класса или класса-наследника, и можно ли приводить указатели на функции-члены независимых классов. К тому времени, когда комитет по стандартизации определился в этих вопросах, различные производители компиляторов уже сделали свои реализации, причем их ответы на эти вопросы различались. В соответствии со стандартом (секция 5.2.10/9), разрешено использование reinterpret_cast для хранения указателя на член одного класса внутри указателя на член независимого класса. Результат вызова функции в приведенном указателе не определен. Единственное, что можно с ним сделать - это привести его назад к классу, от которого он произошел. Я рассмотрю это далее, т.к. в этой области Стандарт имеет мало сходства с реальными компиляторами.

На некоторых компиляторах происходят ужасные вещи, даже при приведении между указателями на члены базового и наследуемого классов. При множественном наследовании использование reinterpret_cast для приведения указателя на фукнцию-член наследуемого класса к указателю на функцию-член базового класса может скомпилироваться, а может и нет, в зависимости от того в каком порядке базовые классы перечислены в объявлении наследника! Вот пример:

class Derived: public Base1, public Base2  // случай А
class Derived2: public Base2, public Base1 // случай Б
typedef void (Derived::*Derived_mfp)();
typedef void (Derived2::*Derived2_mfp)();
typedef void (Base1::*Base1mfp)();
typedef void (Base2::*Base2mfp)();
Derived_mfp x;

В случае А, static_cast<Base1mfp>(x) отработает успешно, а static_cast<Base2mfp>(x) нет. В то время как для случая Б верно противоположное. Вы можете безопасно приводить указатель на функцию член класса-наследника к указателю на функцию-член только первого базового класса! Если вы попробуете все-таки выполнить приведение не к первому базовому классу, MSVC выдаст предупреждение C4407, а Digital Mars C++ выдаст ошибку. Оба будут протестовать против использования reinterpret_cast вместо static_cast, но по разным причинам. Некоторые же компиляторы будут совершенно счастливы, вне зависимости от того, что вы делаете. Будьте осторожны!

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

Также стоит отметить, что Стандарт С++ предоставляет указатели на члены-данные. Они имеют те же операторы и некоторые из особенностей реализации указателей на функции-члены. Они используются в некоторых реализациях stl::stable_sort, но я не знаю других значимых применениях этих указателей.


http://rsdn.org/article/cpp/fastdelegate.xml


 18 
 : Октября 18, 2018, 08:23:38 pm 
Автор kim - Последний ответ от da-nie
Цитировать
Вам следует почитать и изучить вопрос, как хранятся данные при виртуальном наследовании. Тут на английском но все же примерно написано, что не так просто оперировать указателями на члены класса участвующих в таком наследовании.

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

Цитировать
И вообще, резюмируя - если вы пришли к виртуальному наследованию, то с вероятностью близкой к единице вы архитектурно неправильно представляете свое решение.

Это возможно. Ведь в конечном итоге я всё заменил агрегацией. Мне изначально нужно было, чтобы все классы работали с одной общей структурой и при этом совсем не хотелось передавать при инициализации ссылку на эту структуру в каждый класс, а также все функции этих классов были бы унаследованы объединяющим классом (делегирование делать плохо - этих функций сильно дофига, и я потому и разделил их на отдельные классы). Поэтому я определил структуру в базовом классе и от него унаследовал все остальные. Так как наследование было виртуальным, то базовый класс и эта структура в объединяющем классе CError находятся в единственном числе и все классы работают с одним и тем же экземпляром.

Цитировать
Все это можно почитать тут

О, спасибо!
Если я правильно понял,что там написано, гарантируется инициализация переменных до объектов.

 19 
 : Октября 18, 2018, 07:50:37 pm 
Автор kim - Последний ответ от lastcross
Цитировать
Интересная штука получается.
Вот есть у меня базовый класс ...

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

И вообще, резюмируя - если вы пришли к виртуальному наследованию, то с вероятностью близкой к единице вы архитектурно неправильно представляете свое решение.

Цитировать
Кстати, что-то найти не могу порядок инициализации переменных

Все это можно почитать тут

 20 
 : Октября 11, 2018, 07:19:35 pm 
Автор lestat - Последний ответ от da-nie
Кстати, очень спорный сайт www.quizful.net. К примеру, приведена программа и предложено определить результат вычитания двух указателей. Про 32 битную плоскую модель памяти в тексте задачи нет ни слова. Один из вариантов "Это бессмысленно". Вот только верным считается не этот вариант (а размер массива, как результат), хотя давным-давно на том же хабре подробно разобрали, почему нельзя определять размер массива вычитанием указателей.
Ну и куча тестов на простую внимательность (пропустили в комментарии */ звёздочку - попробуй с первого раза заметь Smiley ). Ещё мне встретился массив неопределённой длины в структуре, который вообще-то, помнится, совсем не стандартны и просто фишка ряда компиляторов.

Страниц: 1 [2] 3 4 ... 10