Помогите! Указатели + вектор

Всем здравствуйте! Совсем недавно начал изучать язык с++, и наткнулся на такую проблему

std::vector<std::string> student::get_studnumber(int number) {
            return student_spisok[number];//Вот здесь выдает ошибку 
         }

Хочу сделать список студентов и сохранить все это в векторе + использую другие классы И потом задать каждому оценки и вывести средний бал. Вот мой код

#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>

#include "student.h"

using namespace std;

int main() {
setlocale(0, "");

int i;//количество студентов
string buffer = "";//буфер для записи студентов
vector<string> *students = new vector<string>;//в вектор студенты мы будем записывать фио студента и узнаем их колличество

student *stud = new student;

cout << "Введите ФИО студентов" <<endl;

//добавляем студентов, причем безгранично, пока не будет пустая строка
do {
    getline(cin, buffer);

    if (buffer.size() > 0) {
        students->push_back(buffer);
    }

} while (buffer != "");

i = students->size();//берем количество студентов

stud->set_students(*students);//сохраняем в классе student

//cout << *students[0] <<endl ;
delete students;//удаляем наш вектор со списком, тк этот список сохранен отдельно в классе студенты. Дабы не дублировать этот списко мы будем брать его по команде stud->get_students

//stud->cout_students(stud->get_students());//выводим на экран список
***//cout<<stud->get_students[1];//хочу получать студента из списка что бы проставить ему оценки***

//Ставим оценки


system("pause");
return 0;}

Student.ccp

 #include <string>
 #include <vector>
 #include <iostream>
 #include "student.h"

     //устанавливаем полное имя
     void student::set_fullName(std::string student_fullname) {
        student::full_name = student_fullname;
     }
     //получаем полное имя
     std::string student::get_fullName() {
        return student::full_name;
     }

     void student::set_scores(int student_scores[]) {
        for (int i = 0; i < 5; i++) {
            student::scores[i] = student_scores[i];
        }
     }

     void student::set_averge_ball(float student_averge_ball) {
        student::averge_ball = student_averge_ball;
     }

     float student::get_averge_ball() {
        return student::averge_ball;
     }

     void student::set_students(std::vector<std::string> student_spisok) {
        int size = student_spisok.size();

        for (int i = 0; i < size; i++) {
            student::students.push_back(student_spisok[i]);
        }
     }

     std::vector<std::string> student::get_students() {
        return student::students;
     }

     void student::cout_students(std::vector<std::string> student_spisok) {
        int size = student_spisok.size();

        for (int i = 0; i < size; i++) {
            std::cout <<student_spisok[i] <<std::endl ;
        }
     }


     void student::cout_studnumber(std::vector<std::string> student_spisok) {
        std::cout << student_spisok[1];
     }

     std::vector<std::string> student::get_studnumber(int number) {
        return student_spisok[number];//Вот здесь выдает ошибку 
     }

student.h

#pragma once

#include <string>
#include <vector>

class student
{
public:
//устанавливаем полное имя
void set_fullName(std::string);
    //получаем полное имя
std::string get_fullName();

void set_scores(int[]);

void set_averge_ball(float);

float get_averge_ball();

void set_students(std::vector<std::string>);

std::vector<std::string> get_students();

void cout_students(std::vector<std::string>);

void cout_studnumber(std::vector<std::string>);

std::vector<std::string> get_studnumber(int);

private:

std::string full_name;//ФИО
int scores[5];//5 оценок
float averge_ball;//средний бал
std::vector<std::string> students;
};

Объясните, пожалуйста, можно ли вектора выражать динамически или какая у меня тут ошибка) А может подход вовсе неверный?

С ошибкой всё просто. Во-первых, необъявленная переменная student_spisok. Во-вторых, полагая, что student_spisok — это скорее всего вектор string ФИО студентов, ты в return возвращаешь значение типа string. А в сигнатуре метода у тебя указан тип возвращаемого значения std::vector<std::string>. Нестыковочка. Видимо в сигнатуре метода должно быть std::string.

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

Главная ошибка в твоей программе — это неправильно спроектированный класс. Класс должен представлять атомарную сущность. «Атомарность» определяется текущим уровнем абстракции. Уровни абстракции могут быть представлены по-разному для одной и той же предметной области. Например, может быть такая иерархия абстракций: студент — группа — поток — курс — факультет — ВУЗ. А может быть и такой: студент — ВУЗ. В первом случае студент содержит только сведения об имени (ФИО) и, допустим, оценках; остальные сведения: номер группы, номер потока, номер курса, название факультета будут храниться на других уровнях абстракции. Во втором случае, ВУЗ содержит массив всех студентов, а в каждом студенте есть данные о номере группы, факультете и пр. Но в обоих случаях объект студент — это информация строго по одному учащемуся.

В головной программе ты получаешь от пользователя список имён студентов и сохраняешь его в одном студенте. При этом сам этот студент собственного имени (член full_name) не получает. Затем ты планируешь вводить оценки для всех студентов из списка. Но место под оценки ты выделил только для одного набора оценок (в объекте stud), все остальные «студенты» — это только строки с именами.

Из вышесказанного следует, что студент должен содержать данные только о себе, значит поле std::vector<std::string> students из объявления убираем. Если нам нужно много студентов, значит делаем контейнер, который может хранить несколько студентов. Контейнером может быть массив, вектор, список и т.п. Конкретный вид контейнера надо выбирать исходя из потребностей.

Хорошо, с этим разобрались. Идём дальше: надо привести в порядок набор методов класса. Методы

void set_students(std::vector<std::string>);
std::vector<std::string> get_students();
void cout_students(std::vector<std::string>);
void cout_studnumber(std::vector<std::string>);
std::string get_studnumber(int);

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

Метод void set_scores(int[]) вместе с int scores[5] оставим на совести первоисточника. (Я бы использовал для хранения оценок вектор.)

Странно, что для набора оценок есть метод-сеттер, но нет метода-геттера. Ну и ладно, пока он вроде не нужен. Но в уме галочку поставим...

Самый весёлый метод: void set_averge_ball(float). У студента Иванова текущие отметки (scores) двойки и тройки, а мы тут <бах!> вызвали set_averge_ball и установили Иванову средний балл 4.88. Да?

Одно из правил ООП: данные в объекте должны всегда быть в непротиворечивом состоянии. Метод void set_averge_ball(float) нарушает это правило. Средний балл однозначно зависит от оценок, которые содержатся в переменной scores. Здесь метод-сеттер противопоказан. А вот метод-геттер — вполне законен.

Вычисление среднего балла может быть выполнено двумя способами: либо при добавлении очередной оценки пересчитывается средний балл и запоминается в переменной float averge_ball, либо средний балл вычисляется каждый раз, когда вызывается метод get_averge_ball. Хотя более оптимальным будет третий способ: вычислять среднее арифметическое только при первом вызове get_averge_ball после любого изменения оценок, а потом возвращать кэшированное значение из переменной averge_ball. Кодить хлопотнее, но производительность будет выше. (В данном случае, конечно, на производительность наплевать, но такие маленькие хитрости всегда надо держать в уме.)

Как итог, сделанный на скорую руку в одном файле эскиз программы. (Объявление класса должно быть в отдельном файле, реализации всех методов — тоже должны быть в отдельном файле, как правильно сделано у топикстартера.)

#include <iostream>
#include <string>
#include <vector>
#ifdef _WIN32
#include <Windows.h>
#endif // _WIN32

using namespace std;

class Student {
public:
    Student() : name("Unnamed") { cout << "Student constructor [" << name << "]\n";  }
    Student(const Student& s) { name = s.name; cout << "Student copy constructor [" << name << "]\n"; }
    ~Student() { cout << "Student destructor [" << name << "]\n"; }
    Student& operator = (const Student& s) { name = s.name; cout << "Student operator= [" << name << "]\n"; return *this; }
    void set_name(const string& name) { this->name = name; }
    string get_name() const { return name; }
    void add_score(int sc) { scores.push_back(sc); }
    float get_averge_ball() const;
private:
    string name;
    vector<int> scores;
};

float Student::get_averge_ball() const {
    if (scores.empty())
        return 0.0f;
    int sum = 0;
    for (auto sci = scores.begin(); sci != scores.end(); sci++)
        sum += *sci;
    return sum / (float)scores.size();
}

int main() {
#ifdef _WIN32
    SetConsoleOutputCP(1251);
    SetConsoleCP(1251);
#endif // _WIN32
    string cur_name;
    bool ok;

    Student student;
    vector<Student> group;

    do {
        cout << "Введите имя: ";
        getline(cin, cur_name);
        ok = cur_name != "";
        if (ok) {
            student.set_name(cur_name);
            group.push_back(student);
        }
    } while (ok);

    cout << "Список группы:" << endl;

    for (auto sti = group.begin(); sti != group.end(); sti++) {
        cout << sti->get_name() << endl;
    }

#ifdef _WIN32
    system("pause");
#endif // _WIN32
    return 0;
}

Для интересующихся в качестве бонуса несколько упражнений ;-)

  1. В малый джентльменский набор методов класса я добавил операторы для вывода лога на консоль. Объясните вывод программы.

  2. Можно ли оптимизировать работу с памятью при добавлении студента в группу? Как?

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

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

Воу)
Спасибо большое за такой большой и развернутый ответ. Я пока мало что понял, но надеюсь я дойду до этого) Будем дерзать)

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

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

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.