Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.56/34: Рейтинг темы: голосов - 34, средняя оценка - 4.56
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
1

Вектор объектов класса

04.07.2016, 17:49. Показов 6567. Ответов 16
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Всем привет! Несколько недель назад я писал сюда по поводу одной задачи. Она заключается в том, чтобы реализовать класс "Зоомагазин" с полями: тип животного, пол, цена, количество, а также придумать для этого класса функционал. Я решил сделать функции добавления животных в магазин, просмотр списка животных и их покупка. Для того, чтобы удалять животных из списка при покупке, я решил использовать вектор, в котором будут храниться объекты класса. С вектором возникла проблема, так как при выводе полного списка животных в каждой строке таблицы отображаются данные последнего введённого животного. Когда я использовал массив объектов класса, всё отображалось корректно. Подскажите, как решить эту проблему?

Заголовочный файл:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma once
#ifndef ZOO_H
#define ZOO_H
 
#include <string>
 
using namespace std;
 
class Zoo
{
    private:
        string *type;
        string *gender;
        double *price;
        int *amount;
    public:
        Zoo();
        virtual ~Zoo();
        void Menu();
        void getAll();
        void setAll();
        void table();
};
 
#endif
Исполнительный файл:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include "Zoo.h"
#include <iostream>
#include <iomanip>
 
using namespace std;
 
Zoo::Zoo()
{
    type = new string("animal");
    gender = new string("male");
    price = new double(0);
    amount = new int(0);
}
 
Zoo::~Zoo()
{
 
}
 
void Zoo::Menu()
{
    cout << "Select action." << endl;
    cout << "1. Look all the animals;" << endl;
    cout << "2. Add animal;" << endl;
    cout << "3. Buy animal;" << endl;
    cout << "4. Exit." << endl << endl;
}
 
void Zoo::table()
{
    cout<<endl;
    cout<<"||===========||==========||=========||==========||"<<endl;
    cout<<"||Animal type||  Gender  ||  Price  ||  Amount  ||"<<endl;
    cout<<"||===========||==========||=========||==========||"<<endl;
}
 
void Zoo::getAll()
{
    cout << "||";
    if (type->length()%2==0)
    {
        cout<<setw((11-type->length())/2+type->length())<<*type<<setw((11-type->length())/2+3)<<"||";
    }
    else
    {
        cout<<setw((11-type->length())/2+type->length())<<*type<<setw((11-type->length())/2+2)<<"||";
    }
    if (gender->length()%2==0)
    {
        cout<<setw((10-gender->length())/2+gender->length())<<*gender<<setw((10-gender->length())/2+2)<<"||";
    }
    else
    {
        cout<<setw((10-gender->length())/2+gender->length())<<*gender<<setw((10-gender->length())/2+3)<<"||";
    }
    cout<<setw(9)<<fixed<<setprecision(2)<<*price<<"||";
    cout<<setw(10)<<*amount<<"||";
    cout<<endl<<"||===========||==========||=========||==========||"<<endl;
}
 
void Zoo::setAll()
{
    cout << "Animal type: "; cin >> *type;
    cout << "Gender: "; cin >> *gender;
    cout << "Price: "; cin >> *price;
    cout << "Amount: "; cin >> *amount;
}
Основная программа:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include "Zoo.h"
#include <vector>
 
using namespace std;
 
int main()
{
    cout << "Hello" << endl;
    vector <Zoo> Animal;
    Zoo obj;
    Zoo *ptr_obj = &obj;
    ptr_obj->Menu();
    int enter = 0;
    while (enter != 4)
    {
        cin >> enter;
        switch(enter)
        {
            case 1:
                if (Animal.size() == 0)
                {
                    cout << "There is no animals in the shop now." << endl;
                }
                else
                {
                    ptr_obj->table();
                    for (unsigned int i = 0; i < Animal.size(); i++)
                    {
                        Animal[i].getAll();
                    }
                }
                break;
            case 2:
                ptr_obj->setAll();
                Animal.push_back(obj);
                break;
            case 3:
 
                break;
        }
    }
    return 0;
}
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
04.07.2016, 17:49
Ответы с готовыми решениями:

Вектор объектов класса
Доброго времени суток, мне нужно в цикле заполнить вектор объектов класса с помощью итератора и...

Вектор из объектов класса
Добрый вечер) Подскажите пж, что не так? Вот код программы: В файле class.h: #ifndef...

Вектор объектов класса и алгоритмы
Просветите, пожалуйста, по вопросу: Пусть у нас есть класс, к примеру такой class myclass { int...

Вектор объектов пользовательского класса
Привет всем! Есть класс Automaton, я пытаюсь создать вектор Sample с объектами этого класса, а они...

16
19 / 29 / 13
Регистрация: 09.02.2016
Сообщений: 230
04.07.2016, 19:24 2
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
Zoo::Zoo()
{
type = new string("animal");
gender = new string("male");
price = new double(0);
amount = new int(0);
}
у тебя каждый ссылается на определенный участок памяти, а соответственно и изменяет один участок памяти. вот отчего у тебя все и одинаково

Добавлено через 4 минуты
лучше не играться с динамической памятью, пока обучаешься
1
Модератор
Эксперт С++
13507 / 10757 / 6412
Регистрация: 18.12.2011
Сообщений: 28,714
04.07.2016, 19:31 3
Использование указателей в данной задаче ничем не обосновано.
И, к тому же, Вы совсем не следите за удалением созданных динамических объектов.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <string>
#include <iostream>
#include <iomanip>
#include <vector>
 
using namespace std;
 
class Zoo
{
private:
    string type;
    string gender;
    double price;
    int amount;
public:
    Zoo();
    virtual ~Zoo();
    void Menu();
    void getAll();
    void setAll();
    void table();
};
Zoo::Zoo()
{
    type = "animal";
    gender = "male";
    price = 0;
    amount = 0;
}
 
Zoo::~Zoo()
{
 
}
 
void Zoo::Menu()
{
    cout << "Select action." << endl;
    cout << "1. Look all the animals;" << endl;
    cout << "2. Add animal;" << endl;
    cout << "3. Buy animal;" << endl;
    cout << "4. Exit." << endl << endl;
}
 
void Zoo::table()
{
    cout<<endl;
    cout<<"||===========||==========||=========||==========||"<<endl;
    cout<<"||Animal type||  Gender  ||  Price  ||  Amount  ||"<<endl;
    cout<<"||===========||==========||=========||==========||"<<endl;
}
 
void Zoo::getAll()
{
    cout << "||";
    if (type.length()%2==0)
    {
        cout<<setw((11-type.length())/2+type.length())<<type<<setw((11-type.length())/2+3)<<"||";
    }
    else
    {
        cout<<setw((11-type.length())/2+type.length())<<type<<setw((11-type.length())/2+2)<<"||";
    }
    if (gender.length()%2==0)
    {
        cout<<setw((10-gender.length())/2+gender.length())<<gender<<setw((10-gender.length())/2+2)<<"||";
    }
    else
    {
        cout<<setw((10-gender.length())/2+gender.length())<<gender<<setw((10-gender.length())/2+3)<<"||";
    }
    cout<<setw(9)<<fixed<<setprecision(2)<<price<<"||";
    cout<<setw(10)<<amount<<"||";
    cout<<endl<<"||===========||==========||=========||==========||"<<endl;
}
 
void Zoo::setAll()
{
    cout << "Animal type: "; cin >> type;
    cout << "Gender: "; cin >> gender;
    cout << "Price: "; cin >> price;
    cout << "Amount: "; cin >> amount;
}
 
int main()
{
    cout << "Hello" << endl;
    vector<Zoo> Animal;
    Zoo obj;
    int enter = 0;
    
    do
    {
        obj.Menu();
        cin >> enter;
        switch(enter)
        {
        case 1:
            if (Animal.size() == 0)
            {
                cout << "There is no animals in the shop now." << endl;
            }
            else
            {
                obj.table();
                for (vector<Zoo>::iterator p=Animal.begin();p!=Animal.end(); ++p)
                {
                    p->getAll();
                }
            }
            break;
        case 2:
            obj.setAll();
            Animal.push_back(obj);
            break;
        case 3:
            break;
        }
    }while (enter != 4);
    return 0;
}
1
19 / 29 / 13
Регистрация: 09.02.2016
Сообщений: 230
04.07.2016, 20:07 4
Цитата Сообщение от zss Посмотреть сообщение
за удалением созданных динамических объектов.
вот у меня в vs15 почему-то вылетает, если в я деструкторе удаляю, а когда не удаляю-все норм. лучше на более старую перейти?
0
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
04.07.2016, 20:18  [ТС] 5
Я высвобождал память, но это приводило к ещё какой-то ошибке, видимо одна ошибка привела к другой. Убрал код деструктора в коде для того, чтобы не путаться. Сейчас верну.
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
04.07.2016, 20:29 6
Цитата Сообщение от Хрисипп Посмотреть сообщение
вот у меня в vs15 почему-то вылетает, если в я деструкторе удаляю, а когда не удаляю-все норм.
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
Я высвобождал память, но это приводило к ещё какой-то ошибке, видимо одна ошибка привела к другой.
Всё просто. При удалении одной копии объекта, делается delete в деструкторе,
потом, когда уничтожается другая копия, снова delete и снова для того же адреса.
0
19 / 29 / 13
Регистрация: 09.02.2016
Сообщений: 230
04.07.2016, 20:33 7
Цитата Сообщение от Croessmah Посмотреть сообщение
Всё просто.
точно. подумал об этом, но не был уверен
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
04.07.2016, 22:38 8
Для использования класса в векторе желательно реализовать копирующие конструктор и оператор присваивания и, так как класс ссылается на какие-то ресурсы (указатели, выделение памяти в куче), то не помешали бы перемещающие конструктор и оператор присваивания. См. правило пяти.
1
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
04.07.2016, 23:28  [ТС] 9
Спасибо всем. Проблема решилась. Есть ещё пара вопросов. Я написал функцию покупки животных, в ней пользователю предлагается выбрать животное из списка и указать сколько таких животных он хочет купить. 1) Если он вводит число, превышающее количество таких животных, то отображается оповещение об этом; 2) если он вводит число, совпадающее с количеством животных, то строка с этим животным удаляется из таблицы; 3) если он вводит число, меньшее количества таких животных, то количество должно уменьшиться на это число. Вопросы по 2 и 3 пунктам. 2) Если из вектора можно удалить только последний элемент, то одной строкой удалить не получится и придётся вручную делать (как я и сделал) или всё-таки есть способ? Может быть нужно использовать другой контейнер, но я не припомню такого, где можно было бы удалить любой элемент. Мой способ тоже не работает, он работал с массивом объектов класса, но здесь не хочет. 3) Как у элемента вектора объектов класса уменьшить значение одного поля объекта, т.е. количества (в моей программе ничего не происходит, количество остаётся прежним).

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void Zoo::buy(vector <Zoo> Animal)
{
    if (Animal.size() == 0)
    {
        cout << "There is no animals in the shop now." << endl;
    }
    else
    {
        int enter1, number;
        cout << endl;
        cout << "Enter " << "1" << " to choose an animal." << endl;
        cout << "Enter " << "2" << " to look all the animals." << endl;
        cout << "Enter " << "3" << " to return to the main menu." << endl << endl;
        cin >> enter1;
        switch (enter1)
        {
            case 1:
                cout << "Enter the number of animal which you want to buy" << endl;
                cout << "(enter " << "0" << " to return to the main menu)." << endl << endl;
                cin >> number;
                if (number == 0)
                {
                    break;
                }
                else
                {
                    int k = 0;
                    cout << "How much animals do you want to buy?" << endl;
                    cin >> k;
                    if (k > Animal[number-1].amount)
                    {
                        cout << "There is no " << k << " animals in the shop now.";
                    }
                    else if (k < Animal[number-1].amount)
                    {
                        Animal[number-1].amount-=k;
                        cout << "Thank you for your purchase." << endl;
                    }
                    else
                    {
                        for (unsigned int i = number-1; i < Animal.size()-1; i++)
                        {
                            Animal[i] = Animal[i+1];
                        }
                        cout << "Thank you for your purchase." << endl;
                    }
                }
                break;
            case 2:
                ptr_obj->table();
                for (unsigned int i = 0; i < Animal.size(); i++)
                {
                    Animal[i].getAll();
                }
                break;
        }
    }
}
Добавлено через 5 минут
По поводу деструктора тоже вопрос. Если я не использую динамические переменные, то как высвободить память? Или это нужно только с динамическими переменными?
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
05.07.2016, 09:54 10
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
По поводу деструктора тоже вопрос. Если я не использую динамические переменные, то как высвободить память? Или это нужно только с динамическими переменными?
Если не используете, то не надо освобождать, почитайте про RAII. Также вы можете не объявлять деструктор без надобности (если вам в нём нечего добавить от себя), тогда компилятор сам сгенерирует необходимые конструкторы, операторы присваивания и деструктор. Так же можно компилятору явно указать:
C++
1
2
3
4
5
6
7
8
9
10
11
class Zoo
{
public:
    ...
    Zoo(const Zoo&) = default;  //конструктор копирования по умолчанию
    Zoo(Zoo&&) = default;  //конструктор перемещения по умолчанию
    Zoo& operator=(const Zoo&) = default;  //копирующий оператор присваивания по умолчанию
    Zoo& operator=(Zoo&&) = default;  //перемещающий оператор присваивания по умолчанию
    ~Zoo() = default;  //деструктор по умолчанию
    ....
}
1
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
05.07.2016, 12:06  [ТС] 11
Спасибо, а как изменить поля класса? В моей задаче мне нужно изменить количество животных при покупке, но мой код на поле количества не влияет. Почитал про правило пяти, но хотелось бы пример реализации.
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
05.07.2016, 13:18 12
Лучший ответ Сообщение было отмечено HEYDEPZHUMblU как решение

Решение

Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
В моей задаче мне нужно изменить количество животных при покупке, но мой код на поле количества не влияет.
Т.е. вы вызываете функцию Zoo::buy, покупаете, ещё раз вызываете, выбираете второй пункт меню, а изменение нет? Передавайте вектор по ссылке:
C++
1
2
void Zoo::buy(vector <Zoo> & Animal)
{ ... }
Для вашего случая, если вы отказались от динамических данных, просто не объявляйте деструктор.
Теперь о "правиле пяти"...
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SomeClass
{
public:
    SomeClass() 
    { 
        init_somedata(data); //инициализация ресурса с помощью некоторой функции (создание объекта в куче через "new" тоже по сути инициализация)
    }  
    ~SomeClass() 
    {
        free_somedata(data); //освободили ресурс (или "delete" для объекта полученного через "new")
    }
 
private:
    SomeDataType* data;  //некоторый ресурс, доступный через указатель
}
Вроде бы всё должно быть хорошо, но если написать нечто такое:
C++
1
2
3
4
5
6
7
8
int main()
{
    SomeClass some_object1;  //ничего страшного
    ...
    {  //некоторая область видимости
        SomeClass some_object2 = some_object1;  //вызов дефолтного конструктора копирования, который просто скопирует указатель
    }
}
После выхода из "некоторой области видимости" второй объект уничтожится (вызовется деструктор) освободив ресурс, на который продолжает ссылаться первый объект.
Для того чтобы избежать таких неприятностей нужно определить копирующие конструктор и оператор присваивания:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SomeClass
{
   ...
    SomeClass(const SomeClass& other)
    {
        copy_somedata(data, other.data);  //копирование ресурса
    }
    SomeClass& operator=(const SomeClass& other)
    {
        if (&other != this) {   //не нужно присваивать объект самому себе
            free_somedata(data);
            copy_somedata(data, other.data);
        }
        return *this;
    }
    ...
}
На этом можно и остановиться, но с выходом стандарта C++11 мы можем избегать чрезмерного копирование, которое влечёт использование "тяжелых" функций, как наша copy_somedata, если будем использовать семантику перемещения, там где это нужно, разумеется (это очень обширная тема, излагать её тут нет смысла и времени):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SomeClass
{
    ...
    SomeClass(SomeClass&& other) noexcept  //перемещающий конструктор не должен бросать исключения
    {
        data = other.data;   
        other.data = NULL;   //other - временный объект и использоваться дальше не будет
    }
    SomeClass& operator=(SomeClass&& other) noexcept
    {
        swap(data, other.data);  //стандартная функция, далее временный объект other будет уничтожен
        return *this;
    }
    ~SomeClass()
    {
        if (data)  //добавим проверку на NULL, так как такая ситуация теперь возможна
            free_somedata(data);
    }
    ...
}
1
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
05.07.2016, 16:39  [ТС] 13
Спасибо огромное за объяснение. Программа заработала корректно. А почему, когда я передаю вектор по ссылке, всё работает, а иначе ничего не происходит?
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
05.07.2016, 16:50 14
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
Спасибо огромное за объяснение.
На здоровье.
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
А почему, когда я передаю вектор по ссылке, всё работает, а иначе ничего не происходит?
Потому что передаёте его по значению (т.е. в функцию передаётся копия вектора, которая уже никак не связана с вашим исхоным вектором, плюс это ещё и накладно по процессорным ресурсам)
1
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
05.07.2016, 20:48  [ТС] 15
По поводу деструктора тоже вопрос. Задание заключается в реализации класса, в нём должны быть конструкторы (по умолчанию, c параметрами, копирования), деструктор и свои методы. Я ещё не закончил программу, поэтому конструкторы не все. По условию нужен деструктор, но как мне его использовать, если мои переменные не динамические? Я так понимаю, что можно их сделать динамическими, но тогда и дальнейшие действия с ними подкорректировать.
0
19 / 29 / 13
Регистрация: 09.02.2016
Сообщений: 230
05.07.2016, 22:22 16
Цитата Сообщение от HEYDEPZHUMblU Посмотреть сообщение
По поводу деструктора
а обязательно что-то им делать явно? просто объяви его, да и всё.

Добавлено через 16 минут
ну или ко всему(старому) добавь копирующий конструктор
C++
1
2
3
4
5
6
7
Zoo(const Zoo& z)
{
    type = new string(*(z.type));
    gender = new string(*(z.gender));
    price = new double(*(z.price));
    amount = new int(*(z.amount));
}
ну и конечно в деструкторе delete на каждый
и все будет 0к

Добавлено через 13 минут
и еще одно забыл: также, как и копирующий, определить конструктор перемещения.
C++
1
2
3
4
5
6
7
Zoo(const Zoo&& z)
{
    type = new string(*(z.type));
    gender = new string(*(z.gender));
    price = new double(*(z.price));
    amount = new int(*(z.amount));
}
1
0 / 0 / 0
Регистрация: 06.06.2016
Сообщений: 18
06.07.2016, 09:54  [ТС] 17
Спасибо, добавлю. С копирующим конструктором и конструктором перемещения пока ещё не очень разобрался, почитаю на эту тему.
0
06.07.2016, 09:54
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.07.2016, 09:54
Помогаю со студенческими работами здесь

Отсортировать вектор объектов класса по определенному полю
Нужно отсортировать вектор word объектов objectW по полю length. В 42 строке пытаюсь это сделать,...

Как сделать вектор/массив объектов одного класса с разным <типом>
У меня есть класс MySet&lt;T&gt;, я хочу сделать массив/вектор таких объектов, в который можно будет...

Не удаётся добавить в вектор объектов экземпляры класса (нет подходящего конструктора по умолчанию)
Здравствуйте! Столкнулся со следующей проблемой. Мне нужно создать вектор, который содержит...

Передача объектов дочерних классов через массив объектов родительского класса в функцию
Здравствуйте. Возможно, вопрос больше относится к теории ООП, но все же я не решился задавать его в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru