| Другое > Hard'n'Soft |
| Програмирование на C++ |
| << < (5/25) > >> |
| HoRRoR:
О боже мой, вот вы о чём. Ну это бред, извините, сравнивать вывод в поток и функцию. Почитайте про перегрузку операторов, прежде чем считать использование оператора за функцию. Конечно, можно заставить оператор вызывать функцию, но это уже другой вопрос. |
| gepar:
HoRRoR,ты меня изначально не понял по этому поводу, тут схожесть как в том случае когда друг увидев мой телефон сказал "у меня такой же, только серый и раскладушка" :) Я не о том что си это низкоуровневый язык вроде asm'а или что они реализованы похоже как ты наверное подумал изначально. |
| HoRRoR:
Ты можешь нормальным языком изложить свои мысли? Так, чтобы я однозначно тебя понял? Ты сравниваешь принципиально разные вещи, вот я и хочу узнать, что же ты в них общего нашёл. |
| gepar:
HoRRoR,наличие указателей всюду где только можно и вообще непривычная (для меня) конструкция многих функций. Примеры: scanf ("%d%f",&x,&y) printf("%s", message) |
| HoRRoR:
Где связь вышесказанного с: 1. Схожестью функций си и функций ассемблера? 2. Различием между функциями си и функциями си плюс плюс? Для меня это сейчас как вырванная из контекста фраза. А указатели только там, где они нужны. Тебе надо вникнуть в принципы работы, чтобы понять, что это такое и зачем они нужны. |
| gepar:
HoRRoR,я уже тебе написал что схожесть минимальная и она остаётся схожестью и это лично мне оно похоже. Я отлично понимаю что есть разница между asm кодом вида mov es, ax и кодом на си. --- Цитата: HoRRoR ---2. Различием между функциями си и функциями си плюс плюс? --- Конец цитаты --- Нулевое если учитывать что можно использовать всё что осталось от си. И большое если использовать тот же самый вывод в поток вместо print, ловить ввод через опять же таки поток, а не через get. функции. --- Цитата: HoRRoR ---Тебе надо вникнуть в принципы работы, чтобы понять, что это такое и зачем они нужны. --- Конец цитаты --- Я вник до уровня что они нужны для удобства и экономии памяти. А вообще пустой диалог получается, я так понимаю ты изучил си и знаешь его хорошо и поэтому для тебя он удобен, мне же он не привычен и сравним с asm'ом, который опять же мне не удобен (оба одинаково неудобны и поэтому для меня явл. взаимозаменяемыми понятиями) , а тебе тоже хорошо знаком. Вот потому то тебе и сложно меня понять. Надеюсь хоть в этот раз я более-менее понятно выразился, предлагаю дальше уже не докапываться где же схожесть си и asm'а так как они могут быть похожи лишь внешне и лишь тем кто с ними мало знаком вроде меня :) Добавлено позже: Кстати нашёл удобную кнопочку в Code::Blocks в одной из менюшек для добавления файлов в текущий проект (даже если проекта по сути то и нет), делается всё в 2 клика после того как открыт любой cpp файл, в том числе и любой файл от примера Дейтела. |
| HoRRoR:
Ладно, закроем эту тему, пока мой мозг не вытек в образовавшиеся трещины. --- Цитата ---Я вник до уровня что они нужны для удобства и экономии памяти. --- Конец цитаты --- Ну, иногда. Но дело, в принципе, в другом - без них многие вещи реализовать не представляется возможным и это, можно сказать, часть архитектуры программно-аппаратных решений, даже в каком-то смысле элемент её фундамента. Поработай с памятью напрямую, с объектами классов через указатели на них, с массивами побольше поработай, с динамическими в т.ч. Ещё бы в ассемблер повникать не помешало - когда увидишь, как это работает, то и суть поймёшь. |
| gepar:
--- Цитата: HoRRoR --- Ещё бы в ассемблер повникать не помешало - когда увидишь, как это работает, то и суть поймёшь. --- Конец цитаты --- Ну они указывают на участок памяти, если присвоить им адрес переменной то будут указывать на ту же память где храниться переменная, так? --- Цитата: HoRRoR ---с объектами классов через указатели на них --- Конец цитаты --- Ну я пробовал возвращать значения в виде указателя на элемент класса, но преимуществ не заметил да и Дейтел написал что это опасно и что это плохой стиль так как пользователь может подсунуть потом что угодно и программа из-за этого будет некорректно работать :) Кстати: а может ли функция возвращать сразу несколько значений? Мне с самого начала было интересно. И ещё я тут Дейтела пример рассматриваю и вот у него некоторые строчки без комментов, прокомментируйте пожалуйста (это конструктор если что): --- Код: ---Employee::Employee( const char * const first, const char * const last, //W const Date &dateOfBirth, const Date &dateOfHire ) //T : birthDate( dateOfBirth ), //F hireDate( dateOfHire ) //? --- Конец кода --- Конкретно интересует первая строчка, это 2 константных указателя на константные данные, wright? Ну а 3я и 4я как я понимаю это инициализация уже переменных класса. |
| HoRRoR:
--- Цитата ---Ну они указывают на участок памяти, если присвоить им адрес переменной то будут указывать на ту же память где храниться переменная, так? --- Конец цитаты --- Всё верно, но мне кажется, что ты всё же не совсем понимаешь их предназначение. --- Цитата ---Ну я пробовал возвращать значения в виде указателя на элемент класса, но преимуществ не заметил да и Дейтел написал что это опасно и что это плохой стиль так как пользователь может подсунуть потом что угодно и программа из-за этого будет некорректно работать --- Конец цитаты --- Глупости какие-то Дейтел говорит. В кривых руках всё опасно. Как ещё организовывать передачу объектов класса, как не через указатели? Вариант, конечно, через ссылки, но это не всегда удобно. Создавать копии или временные экземпляры в большинстве случаев не имеет смысла. --- Цитата ---Кстати: а может ли функция возвращать сразу несколько значений? Мне с самого начала было интересно. --- Конец цитаты --- Да, но через return только одно. Ты можешь передавать значения по указателям или ссылкам. --- Код: ---void foo_ref(int &a1, int &a2) { a1 = 1; a2 = 2; } // Более опасно, желательно проверять на NULL: void foo_ptr(int *a1, int *a2) { *a1 = 1; *a2 = 2; } --- Конец кода --- --- Цитата ---Конкретно интересует первая строчка, это 2 константных указателя на константные данные, wright? Ну а 3я и 4я как я понимаю это инициализация уже переменных класса. --- Конец цитаты --- Всё верно. --- Код: ---Employee::Employee( const char * const first, // Константный указатель на константный char (ни указатель, ни данные по нему менять нельзя) const char * const last, // Константный указатель на константный char const Date &dateOfBirth, const Date &dateOfHire ) // Константные ссылки на переменные типа Date : birthDate( dateOfBirth ), // birthDate = dateOfBirth hireDate( dateOfHire ) // hireDate = dateOfHire --- Конец кода --- Ссылка - это что-то вроде второго имени для существующей переменной. По сути это как бы автоматический указатель, который нельзя менять - он всегда указывает на переменную, для которой был создан и автоматически разадресовывается. Т.е. работать с ней можно как с переменной, на которую она указывает. Иногда имеет смысл передавать переменные через ссылки (например, когда указатели не совсем удобны) - тогда не создаются копии переменных. Ну, или когда необходимо менять переменную внутри вызываемой функции. |
| gepar:
HoRRoR, ага, с const понял, спасибо. Нуждаюсь в хорошей статье по динамическим структурам (Дейтел писал только об обычных, а мне нужно сделать лабораторную по динамическим) может у кого завалялась ссылка, только на англ. не подойдёт информация. У меня ещё по ним пример есть в лабе, но он мне как-то не особо понятен. --- Код: ---#include <iostream.h> struct Node{ int d; //информационное поле Node *next; //указатель на следующий Node *prev; //указатель на предыдущий }; /*описание прототипов функций */ Node * first(int d); //инициализация списка (создание первого эл-та) void add(Node **tail, int d); //добавление в конец Node * find(Node * const head, int i); //поиск по ключу элемента bool remove(Node **head, Node **tail, int key); //удаление Node * insert(Node * const head, Node **tail, int key, int d); //вставка элемента в список //---- int main() { // Формирование первого элемента списка Node *head = first(l); // устанавливаем указатель на конец на первый элемент списка Node *tail = head; // Добавление в конец списка четырех элементов 2, 3, 4, и 5: for (int i = 2; i<6; i++) add(&tail, i); // Вставка элемента 200 после элемента 2: insert(head, &tail, 2, 200); // Удаление элемента 5: if(!remove (&head, &tail, 5)) cout << "не найден"; // вывод списка на экран Node *pv = head; while (pv){ cout << pv->d << ' '; pv = pv->next; } return 0; } //описание функций // Формирование первого элемента (инициализация списка) Node * first(int d) { Node *pv = new Node; //выделение памяти под первый элемент pv->d = d; //заполнение информационной части pv->next = 0; //формирование указателя на следующий (на конец) pv->prev = 0; //формирование указателя на предыдущий (на конец) return pv; //возврат указателя на голову (первый элемент) } // Добавление в конец списка void add(Node **tail, int d) { Node *pv = new Node; //выделение памяти под создаваемый элемент pv->d = d; //заполнение инф. поля pv->next = 0; //формирование указателя на следующий (конец) pv->prev = *tail; //формирование указателя на предыдущий (*tail)->next = pv; //изменение указателя на следующий предыдущего элемента *tail = pv; //изменение указателя на хвост } // Поиск элемента по ключу Node * find(Node * const head, int d) { Node *pv = head; //берем первый элемент списка while (pv) //просматриваем список до конца { if(pv->d == d)break; //если нашли элемент с соответствующим ключом, выходим из цикла pv = pv->next; //переходим к следующему элементу } return pv; //возвращаем указатель на найденный элемент, //если искомый элемент не найден – возвращаемое значение pv=0 } // Удаление элемента bool remove(Node **head, Node **tail, int key) { if(Node *pkey = find(*head, key)){ // 1 if (pkey == *head){ // 2 *head = (*head)->next; (*head)->prev =0;} else if (pkey == *tail){ // 3 *tail = (*tail)->prev; (*tail)->next = 0;} else{ // 4 (pkey->prev)->next = pkey->next; (pkey->next)->prev = pkey->prev;} 6 delete pkey; return true; // 5 } return false; // 6 } // Вставка элемента после заданного Node * insert(Node * const head, Node **tail, int key, int d) { if(Node *pkey = find(head, key)) //если заданный элемент найден { Node *pv = new Node; //выделяем память под новый элемент pv->d = d; //заполняем инф. поле // 1 - устанавливаем связь нового узла с последующим: pv->next = pkey->next; // 2 - устанавливаем связь нового узла с предыдущим: pv->prev = pkey; // 3 - устанавливаем связь предыдущего узла с новым: pkey->next = pv; // 4 - устанавливаем связь последующего узла //(не последнего !) с новым: if( pkey != *tail) (pv->next)->prev = pv; //иначе обновление указателя на конец списка, //так как узел вставляется в конец: else *tail = pv; return pv; //возвращаем указатель на вновь созданный элемент } return 0; } --- Конец кода --- Например мне непонятно что такое void add(Node **tail, int d); и прочие строки где есть по две звёздочки, что это? Не указатель на указатель на элемент же :) |
| HoRRoR:
Впервые встречаю понятие "динамические структуры". То ли понятие неверное, то ли перевод кривой. Динамических структур в C++ быть не может в принципе, т.к. структура - это железно заданный порядок данных или нескольк заданных порядков (если присутствуют объединения или т.п.). Есть динамические объекты - это переменные типов этих структур, при том динамически создаваемые, но никак не динамические структуры. В примере у тебя - двусвязный список, когда каждый элемент списка указывает на предыдущий и следующий элементы. Две звёздочки - это как раз указатель на указатель, потому как указатель - тоже тип данных. Три звёздочки - указатель на указатель на указатель. Всё предельно просто, если не заморачиваться. Для лучшей читаемости кода иногда создают новый тип через typedef, тогда не придётся писать много звёздочек. Я не вникал в код, но мне не совсем понятно, зачем в коде нужен указатель на указатель, потому как обычно вполне достаточно просто указателя на родительский (основной) элемент. Вот мой кусок кода из лабораторной по двусвязным спискам: --- Код: ---template <typename t, int base_hash> ListItem<t, base_hash>* ListItem<t, base_hash>::AddToTail(t data) { count++; if(next) return next->AddToTail(data); ListItem* item = new ListItem(data); next = item; item->prev = this; return item; } --- Конец кода --- Тут в процессе участвует только указатель (this), но не указатель на указатель (зачем?). Здесь по цепочки просматривается весь список и находится последний элемент, к которому и прицепляется добавляемый элемент. Можно было избежать рекурсивного вызова функций, сделав цикл. |
| gepar:
--- Цитата: HoRRoR ---Я не вникал в код, но мне не совсем понятно, зачем в коде нужен указатель на указатель, потому как обычно вполне достаточно просто указателя на родительский (основной) элемент. --- Конец цитаты --- Значит всё же это именно указатель на указатель в примере, понятно. |
| gepar:
HoRRoR, а что делает в твоём коде операция стрелка? |
| HoRRoR:
Разадресация. ptr->field === (*ptr).field. |
| gepar:
А как совершить вставку одной структуры в другую, но не в конец, а где-то по середине. Что-то не могу сообразить как делать потом сдвиг элементов в первой структуре. |
| HoRRoR:
Ничего не понял. Какой ещё сдвиг элементов? Структура в структуре - дело элементарное. Ничем не отличается от переменной в структуре. --- Код: ---struct st1 { int a; int b; }; struct st2 { float c; st1 s; float d; }; --- Конец кода --- или --- Код: ---struct st2 { float c; struct { int a; int b; } s; float d; }; --- Конец кода --- |
| gepar:
HoRRoR, задание вообще полностью такое: написать программу с функциями которая: 1)Вставляет в список P за последним вхождением элемента E все элементы списка P1, если E входит в P; 2)Удаляет из непустых слов списка P их первые буквы; 3)Добавляет в конец списка P инвертированный список P. Тоесть в 1 я должен найти последнее вхождение этого E, пихнуть как-то туда P1, а то что после в списке P было изначально сдивинуть в конец чтобы оно было после всех элементов P1 как я понимаю. Иными словами: Список P: 1 2 3 4 5 6 7 8 9; Список P1: a b c d e f g; Элемент E например 3, в итоге должно получиться что список P: 1 2 3 a b c d e f g 4 5 6 7 8 9. Как подвинуть эти 4 5 6 7 8 9, я что-то не могу никак представить. |
| HoRRoR:
При чём тут структуры вообще? А сдвиг - это элементарно (если речь идёт о массиве) - копируешь в цикле сдвигаемые элементы, начиная с последнего сдвигаемого, на позицию, увеличенную на значение сдвига. Но у тебя двусвязный список, насколько я понимаю, поэтому тебе сдвигать ничего не надо, надо лишь переставить указатели. "3" у тебя должен начать указывать на "a", "а" обратно на "3", "g" на "4", "4" обратно на "g". Схематично: <- - связь назад -> - связь вперёд <--> - двусторонняя взаимная связь Т.е., у тебя было два списка: --- Код: ---(пусто) <- 1 <--> 2 <--> 3 <--> 4 <--> 5 <--> 6 <--> 7 <--> 8 <--> 9 -> (пусто) (пусто) <- a <--> b <--> c <--> d <--> e <--> f <--> g -> (пусто) --- Конец кода --- Тебе надо сделать примерно так: --- Код: ---(пусто) <- 1 <--> 2 <--> 3<- -> 4 <--> 5 <--> 6 <--> 7 <--> 8 <--> 9 -> (пусто) \-> a <--> b <--> c <--> d <--> e <--> f <--> g -/ --- Конец кода --- Т.е., по шагам: 1. Связь вперёд у "3" заменить на "а". 2. Связь назад у "а" заменить "3". 3. Связь вперёд у "g" заменить на "4". 4. Связь назад у "4" заменить на "g". |
| gepar:
HoRRoR,спасибо, задача сама прояснилась теперь, я изначально воспринимал эти новые "динамические" элементы стуктуры немного иначе (больше как элементы массива нежели как куча указателей :) ). А можешь пожалуйста скопировать свой код лабораторной по двусвязным спискам полностью (если он у тебя по структурам и затрагивает создание/удаление элементов структуры, а не какие-то другие операции), подозреваю что он у тебя будет понятнее мне примера преподши что у меня сейчас есть. Жаль у Дейтела нет по структурам с динам. данными отдельного раздела, мне нравиться его стиль изложения и его код понимать мне не сложно. |
| HoRRoR:
У меня лабораторная не по спискам была, а с их использованием. Там используются шаблоны и прочие ужасы (по крайней мере, тебе так может показаться), да и использование списка ограничено парой операций. Лучше тебе так простенький пример состряпаю: --- Код: ---#include <iostream> struct ListItem { std::string data; // Хранимые в элементе списка данные ListItem *prev, *next; // Указатели на предыдущий и следующий элементы // Конструктор ListItem(const std::string data):prev(NULL),next(NULL) { this->data = data; } // Возвращает последний элемент списка ListItem* getTail() { ListItem* item = this; while(item->next) item = item->next; return item; } // Добавляет в конец списка новый элемент ListItem* addToTail(const std::string data) { ListItem *item = new ListItem(data), *last = getTail(); item->prev = last; last->next = item; return item; } // Находим последний элемент с указанным значением ListItem* findLastItem(const std::string data) { ListItem* item = this, *last = NULL; while(item->next) { if(item->data == data) { last = item; } item = item->next; } return last; } // Вставляем список после последнего элемента с указанным значением // Если элемент не найден - возвращаем false bool insertAfterLastItem(const std::string data, ListItem* list) { ListItem* last = findLastItem(data), *next_item, *prev_item, *list_last; if(!last) return false; next_item = last->next; list_last = list->getTail(); // b1 <--> b2 <--> b3 <--> b4 -> завставляем указывать на a4 list_last->next = next_item; // Если на месте a4 не NULL, то заставляем a4 указывать на b4 // b1 <--> b2 <--> b3 <--> b4 <--> a4 if(next_item) next_item->prev = list_last; // Заставляем b1 (prev) указывать на a3 // a1 <--> a2 <--> a3 <--> a3 <- b1 <--> b2 <--> b3 <--> b4 <--> a4 // \_____________________________/ list->prev = last; // Заставляем a3 (next) указывать на b1 // a1 <--> a2 <--> a3 <--> a3 <--> b1 <--> b2 <--> b3 <--> b4 <--> a4 last->next = list; return true; } // Декструктор - уничтожаем по цепочке все элементы списка ~ListItem() { if(next) delete next; } }; void print_list(ListItem* list) { ListItem* item = list; while(item) { std::cout << item->data; item = item->next; if(item) std::cout << " <--> "; } std::cout << std::endl; }; int main() { ListItem *a = new ListItem("a1"); a->addToTail("a2"); a->addToTail("a3"); a->addToTail("a3"); a->addToTail("a4"); std::cout << "List a: "; print_list(a); ListItem *b = new ListItem("b1"); b->addToTail("b2"); b->addToTail("b3"); b->addToTail("b4"); std::cout << "List b: "; print_list(b); a->insertAfterLastItem("a3", b); std::cout << "Merged: "; print_list(a); // Т.к. все элементы теперь в одном списке, уничтожаем этот список разом со всеми элементами delete a; return 0; } --- Конец кода --- |
| Навигация |
| Главная страница сообщений |
| Следующая страница |
| Предыдущая страница |