Автор Тема: Програмирование на C++  (Прочитано 76691 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #90 : 17 Февраль 2011, 23:52:19 »
Впервые встречаю понятие "динамические структуры". То ли понятие неверное, то ли перевод кривой. Динамических структур в 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

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #91 : 18 Февраль 2011, 09:15:58 »
Цитата: HoRRoR
Я не вникал в код, но мне не совсем понятно, зачем в коде нужен указатель на указатель, потому как обычно вполне достаточно просто указателя на родительский (основной) элемент.
Значит всё же это именно указатель на указатель в примере, понятно.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #92 : 18 Февраль 2011, 19:41:03 »
HoRRoR, а что делает в твоём коде операция стрелка?

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #93 : 18 Февраль 2011, 20:37:13 »
Разадресация.
ptr->field === (*ptr).field.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #94 : 20 Февраль 2011, 13:42:08 »
А как совершить вставку одной структуры в другую, но не в конец, а где-то по середине. Что-то не могу сообразить как делать потом сдвиг элементов в первой структуре.

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #95 : 20 Февраль 2011, 14:48:24 »
Ничего не понял. Какой ещё сдвиг элементов?
Структура в структуре - дело элементарное. Ничем не отличается от переменной в структуре.

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

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #96 : 20 Февраль 2011, 15:25:30 »
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

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #97 : 20 Февраль 2011, 15:45:00 »
При чём тут структуры вообще?
А сдвиг - это элементарно (если речь идёт о массиве) - копируешь в цикле сдвигаемые элементы, начиная с последнего сдвигаемого, на позицию, увеличенную на значение сдвига.
Но у тебя двусвязный список, насколько я понимаю, поэтому тебе сдвигать ничего не надо, надо лишь переставить указатели. "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

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #98 : 20 Февраль 2011, 16:26:11 »
HoRRoR,спасибо, задача сама прояснилась теперь, я изначально воспринимал эти  новые "динамические" элементы стуктуры немного иначе (больше как элементы массива нежели как куча указателей  :) ).
А можешь пожалуйста скопировать свой код лабораторной по двусвязным спискам полностью (если он у тебя по структурам и затрагивает создание/удаление элементов структуры, а не какие-то другие операции), подозреваю что он у тебя будет понятнее мне примера преподши что у меня сейчас есть. Жаль у Дейтела нет по структурам с динам. данными отдельного раздела, мне нравиться его стиль изложения и его код понимать мне не сложно.
« Последнее редактирование: 20 Февраль 2011, 16:29:00 от gepar »

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #99 : 20 Февраль 2011, 18:03:43 »
У меня лабораторная не по спискам была, а с их использованием. Там используются шаблоны и прочие ужасы (по крайней мере, тебе так может показаться), да и использование списка ограничено парой операций. Лучше тебе так простенький пример состряпаю:
#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;
}


Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #100 : 21 Февраль 2011, 11:54:41 »
Спасибо за пример, он мне более понятный. Не знал что у структур тоже может быть конструктор . Я только с this немного ознакомлен и не могу понять на что он в этом случае указывает:
ListItem* item = this; (строка 17).
Я пока встречал только применение вида this->x и (*this).x ну и не явное использование его.

Оффлайн ALKO

  • Пользователь
  • Сообщений: 179
  • Пол: Мужской
  • Петросян он и на эму-лэнде Петросян.
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #101 : 21 Февраль 2011, 15:07:29 »
подскажите плиз,умоляю,аааа ползаю на коленях :'(
подробную инструкцию,как добавить внешний файл в экзэшник,знаю что через ресурсы,но как именно хз.
и как его потом вызывать

Добавлено позже:
на С++ Builder

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #102 : 21 Февраль 2011, 17:50:19 »
Цитата
Спасибо за пример, он мне более понятный. Не знал что у структур тоже может быть конструктор . Я только с this немного ознакомлен и не могу понять на что он в этом случае указывает:
В C++ структуры и классы различаются лишь принципом инкапсуляции - у классов все элементы по умолчанию приватные, у структур - публичные. Т.е. классы соответствуют парадигмам ООП. В чистом си конструкторов быть не может в принципе.
this указывает на текущий элемент. Т.е. если мы создали элемент ListItem, например, ListItem *item = new ListItem(""), то внутри него this равен этому item. Также внутри методов нет разницы, писать this->x или x. Вернее, есть, если существует переменная x, которая перекрывает поле x, тогда придётся писать this->x, чтобы дать понять, что обращение идёт именно к полю, а не к существующей внутренней переменной.
Чтобы было понятней:
class Sample
{
    int x, y;
    void foo()
    {
        x += 4; // Можно написать this->x - разницы нет
    }
    void setX(int x)
    {
        this->x = x; // Т.к. у нас появилась переменная x, то обращение будет именно к ней.
                     // Поэтому надо писать this->x, чтобы получить доступ к полю.
    }
};

ALKO, пишешь resource script (*.rc), компилируешь его, затем через #pragma resource или как-то так аттачишь. Точно не помню, чуть позже расскажу подробней - у меня где-то исходники валяются.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #103 : 22 Февраль 2011, 00:51:40 »
Ну чтоже, с заданием лабораторной справился, но не чувствую я уверенности в том что я овладел полностью указателями (неуверенность пришла какраз после этой лабораторной).

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #104 : 22 Февраль 2011, 16:19:25 »
Судя по тому как принимает лабораторные преподша вывода 5и строчек через cout создающих имитацию работы программы было бы достаточно  :)

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #105 : 27 Февраль 2011, 18:38:48 »
И опять я с этими указателями  :)
Что не так у меня в коде? При попытке вызвать функцию материться мол не может сконвертировать двойной указатель в одинарный.

int main()
{
    int *pAbc=new int;
...
    maximum(a,n,&pAbc);
    cout<<"abc="<<*pAbc;
return 0;
}

void maximum(int a[], int n, int *abc)
{
    *abc=a[1];
    for (int i=0;i<n;i++)
     if (a[i]>*abc)
      *abc=a[i];
}
Весь код не привожу чтобы не запутывать, хотя могу его полностью привести. Это я опять где-то затупил в одном месте и не замечаю наверное.
« Последнее редактирование: 27 Февраль 2011, 18:42:31 от gepar »

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #106 : 27 Февраль 2011, 18:46:20 »
Э, а зачем ты берёшь адрес указателя? Вот у тебя и получается указатель на указатель - &pAbc. И динамически создавать ОДИН инт - верх непрактичности, разве что только если ты это в познавательных целях делаешь. Только вот ты его уничтожить забываешь через delete - утечка памяти.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #107 : 27 Февраль 2011, 19:22:08 »
HoRRoR,да до удаления ещё не дошло, я его передать никак не мог потому минипрограмма в стадии разработки  :) Я уже вспомнил что адрес мне нужно было бы кинуть если бы у меня pAbc было просто число int например, а не указатель, вот знал же что где-то жёстко туплю.

Добавлено позже:
Теперь возник другой вопрос, почему всё что я мудрю с указателем в функции не изменяет реальный указатель? Неужто я и правда так плохо знаком с указателями :(
int main()
{
    int *pAbc;
    int a[n];
...
    pAbc=&a[0];// я делаю это чтобы хоть какое-то значение было в pAbc.
    max=maximum (a,n,pAbc);// по идеи pAbc тоже ведь должен измениться
    cout<<"abc="<<*pAbc; // выводится a[0] несмотря на то что в maximum() было якобы изменение pAbc
...

int maximum(int a[], int n, int *abc)
{
    int max=0; // max - не максимальный элемент, а сумма элементов что в массиве идут до максимального элемента
    abc=&a[0];
    for (int i=0;i<n;i++)
     if (a[i]>*abc)
      abc=&a[i];
    for (int i=0; &a[i]!=abc;i++) // да, глупо я придумал наверное, ну да суть не в этом
     max+=a[i];
    abc=&a[9];// добавил для проверки что pAbc не изменяеться (в массиве 10 элементов), при выводе в main выводиться не a[9]
    return max;
}
Могу предоставить полный код если это нужно.

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #108 : 27 Февраль 2011, 19:30:03 »
Ты всё совсем не так понимаешь. Передаваемые объекты копируются, и в функции используются их локальные копии. Исключение составляют статические массивы - для них создаётся переменная-указатель на первый элемент массива. Т.е. факт того, что переданная переменная имеет тип указателя, совсем не делает его изменяемым. Компилятору всё равно, какой ты тип передаёшь - правила одинаковы для всех.
Поэтому, если ты хочешь что-то менять, передавай указатель на это - тогда из функции ты сможешь получить доступ к тому, на что он указывает. Если хочешь менять указатель - передавай указатель на него. Либо же передавай переменные по ссылкам.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #109 : 27 Февраль 2011, 19:40:57 »
HoRRoR, ммм, тоесть у меня передаётся функции сам массив а, копия n и копия указателя ?

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #110 : 27 Февраль 2011, 19:53:33 »
Про статические массивы я писал в предыдущем сообщении. Копия n и копия указателя - да, верно.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #111 : 27 Февраль 2011, 19:58:48 »
Так это что получается что при желании обычный элемент я могу передать по ссылке в функцию для редактирования, а указатель - нет (нужно создавать указатель на указатель), так нИчестна  :)

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #112 : 27 Февраль 2011, 20:02:09 »
Что тебе мешает передать указатель по ссылке? Я ещё раз говорю - нет разницы, является ли тип передаваемого объекта указателем. Тип есть тип, это унифицированное понятие как для человека, так и для компилятора.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #113 : 27 Февраль 2011, 20:07:20 »
HoRRoR,так а как это сделать? Если я буду передавать как третий аргумент при вызове функциии &pAbc то будет опять та ошибка с двойным указателем. Как тогда функции дать знать что ей будет передана ссылка на указатель подскажи пожалуйста ?

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #114 : 27 Февраль 2011, 20:12:50 »
Эм... &переменная - взятие адреса, а не передача по ссылке. Ссылку в данном случае можно описать только в прототипе самой функции - int* &ref. Если в прототипе объявлена обычная переменная, но функции передаётся ссылка - толку от этого никакого.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #115 : 27 Февраль 2011, 20:31:37 »
HoRRoR, я понял, читаю дальше Дейтела, я пока о &ref первый раз услышал  :) Попробовал добавить в прототип приписку &ref не изменяя больше код - получил ошибку
main.cpp||undefined reference to `maximum(int*, int, int*&)'|
В общем наверное стоит ещё почитать Дейтела дальше чтобы понять как пользоваться этой &ref.

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #116 : 27 Февраль 2011, 20:43:47 »
Не понял, что, как и куда ты приписал. Код покажи.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #117 : 27 Февраль 2011, 20:59:11 »
В прототипе исользуемой функции получилось:
int maximum(int [], int, int* &ref);
Но я уже и так понял что нужно продолжать читать Дейтела, рановато я решил баловаться указателями, когда не все фишки связанные с ними знаю.

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #118 : 27 Февраль 2011, 21:42:43 »
Объявлено всё правильно, ты при вызове что-то намудрил, видимо, что функция с соответствующим прототипом не найдена. Покажи код вызова и объявления передаваемых переменных.

Оффлайн gepar

  • Пользователь
  • Сообщений: 10150
  • Пол: Мужской
  • ▂ ▃ ▄ ▅ ▆ ▇ █
    • Просмотр профиля
Re: Програмирование на C++
« Ответ #119 : 27 Февраль 2011, 21:50:48 »
HoRRoR, ну всё остальное я оставил как и было, те:
int maximum(int [], int, int* &ref); //прототип maximum
...
int main()
{
    int *pAbc;
    int max;
...
    max=maximum (a,n,pAbc);
    cout<<"abc="<<*pAbc;
    return 0;
}

int maximum(int a[], int n, int *abc)
{
    int max=0;
    abc=&a[0];
    for (int i=0;i<n;i++)
     if (a[i]>*abc)
      abc=&a[i];
    for (int i=0; &a[i]!=abc;i++)
     max+=a[i];
    abc=&a[9];
    return max;
}