Szukanie obiektu posiadającego wprowadzony atrybut w wektorze struktur

0

Cześć,
mam taka strukturę:

struct sStudent {
public:
	//atrubuty
	string imie;
	string nazwisko;
	int rok_urodzenia;
	vector<pair<string, float>> oceny;


	//metody
	void pobierz();
	void pobierz_oceny(vector<sStudent> studenci);
	void wyswietl();
	void wyswietl_oceny();
	sStudent zwroc();

};

i mam

vector <sStudent> studenci;

metoda

void sStudent::pobierz_oceny(vector<sStudent> studenci);

raczej jest poprawna.
Problem leży w linijce gdzie do iteratora "itr" (który potem pozwoli mi przypisać oceny za pomocą funkcji "pobierz_oceny" do odpowiedniego studenta z vectora "studenci") chcę przypisać wartość zwracaną przez funkcje find(). Wiem już, że find() działa tylko dla typów wbudowanych, a nie do stworzonych struktur, ale w takim razie jak zapisać pierwszy i drugi argument funkcji find() aby odnieść się do przeszukiwania vectora stringów "nazwisko" jako atrybutów struktur "student". Próbowałem napisać również swoją funkcję która by rozwiązywała problem, nie posługując się algorytmami STL-a , ale jedyne co moja funkcja zdołała ustalić to to czy student o wprowadzonym nazwisku istnieje w vectorze "studenci", nie potrafiła zwrócić iteratora vektorowego do tego studenta.
oto fragment kodu:

cout << "Podaj nazwisko studenta, ktorego oceny chcesz wprowadzic" << endl;
			string nazwisko;
			cin.ignore();
			getline(cin, nazwisko);
			vector<sStudent>::iterator itr; 

			itr = find(studenci.begin(), studenci.end(), nazwisko); //  tutaj jest blad
			system("cls");
			if (itr == studenci.end()) { cout << "Blad wprowadzania, nie istniejacy student" << endl; system("pause"); system("cls"); break; }
			else { cout << "Student znaleziony" << endl; }
			itr->pobierz_oceny(studenci);
1

raczej jest poprawna.

Nie jest, bo przekazujesz vector przez wartość, a nie przez stałą referencję.
Funkcja się nazywa "pobierz", a nic nie zwraca.

To jest problem XY! Pytasz jak naprawić twoje rozwiązanie, zamiast opisać co chcesz rozwiązać (jaką chcesz mieć funkcjonalność).
Co gorsze można się tylko domyślać co jest ci potrzebne (pewnie lambda).

0

Tak racja, trzeba przekazać to przez referencje (trochę mi głupio tym bardziej że miałem to na zajęciach).

void pobierz_oceny(const vector<sStudent> &studenci);

jeżeli przekażę vektor sStudent - ów przez stałą referencję to wewnątrz tej metody będę mógł modyfikować vector oceny, który jest atrybutem tej struktury w vectorze??
czyli inaczej czy stałość (const) tej referencji tyczy się tylko zawartości vektora studenci(np. nie mogę dodawać studentów, zmieniać ich kolejności) czy atrybutów tych studentów też??
PS. Dopiero zaczynam oswajać się z forum, dziękuję za obiektywną krytykę dotyczącą konstruowania wątków.

0

Chciałem napisać program który z perspektywy użytkownika wyglądał by tak, że w case 1 wprowadza kolejno studentów : imię, nazwisko, rok urodzenia. W drugim case (czyli z perspektywy użytkownika 2 segmencie menu) wpisywałby on nazwisko studenta, następnie program sprawdza czy jest on wpisany na listę studentów, jeżeli tak to pojawia się opcja dodania ocen. No i program dodaje oceny do wektora ocen studenta z wprowadzonym nazwiskiem.
Oto tym razem mój cały kod:

#include "header.h"

int main()
{
	vector<sStudent> studenci;
	for (;;)
	{
		int wybor = menu();
		switch (wybor) {
		case 1: {
			int n;
			cout << "Ilu studnetow chcesz wprowadzic : ";
			cin >> n;
			system("cls");

			for (int i = 0; i < n; i++) {
				sStudent s1;
				studenci.push_back(s1.zwroc());
			}
			system("pause");
			system("cls");
			break;
		}

		case 2: {

			cout << "Podaj nazwisko studenta, ktorego oceny chcesz wprowadzic" << endl;
			string nazwisko;
			cin.ignore();
			getline(cin, nazwisko);
			vector<sStudent>::iterator itr;

			itr = find(studenci.begin(), studenci.end(), nazwisko); //  tutaj jest blad
			system("cls");
			if (itr == studenci.end()) { cout << "Blad wprowadzania, nie istniejacy student" << endl; system("pause"); system("cls"); break; }
			else { cout << "Student znaleziony" << endl; }
			itr->pobierz_oceny(studenci);

			system("pause");
			system("cls");
			break;
		}
		default: {
			cout << "PODAJ POPRAWNA LICZBE !!!" << endl;
			cin.ignore();
		}
		}
	}
	system("pause");
	return 0;
}

To funkcje.cpp

#include "header.h"


int menu() {
	int liczba;
	cout << "MENU : " << endl;
	cout << "1 - Wprowadzanie danych studentow" << endl;
	cout << "2 - Wprowadzanie ocen studentow" << endl;
	cout << "Podaj liczbe : "; cin >> liczba;
	system("cls");
	return liczba;
}

void sStudent::pobierz() {

	cout << "Dodawanie studenta" << endl;
	cout << "Podaj imie : ";
	cin >> imie;
	cout << "Podaj nazwisko : ";
	cin >> nazwisko;
	cout << "Podaj rok urodzenia : ";
	cin >> rok_urodzenia;
	system("cls");
}

void sStudent::pobierz_oceny(const vector<sStudent>& studenci) {

	int n;
	cout << "Ile ocen chcesz wprowadzic : ";
	cin >> n;
	for (int i = 0; i < n; i++) {
		pair<string, float> ocena;
		cout << "Podaj nazwe przemiotu : ";
		cin.ignore();
		getline(cin, ocena.first);
		if (ocena.first == " ") break;
		cout << "Podaj ocene : ";
		cin >> ocena.second;
		oceny.push_back(ocena);
	}
	system("cls");
}

void sStudent::wyswietl() {
	cout << "Imie : " << imie << endl;
	cout << "Nazwisko : " << nazwisko << endl;
	cout << "Rok urodzenia : " << rok_urodzenia << endl;
}

void sStudent::wyswietl_oceny() {

	for (auto& el : oceny) {
		cout << el.first << endl;
		cout << el.second << endl;
	}

}

sStudent sStudent::zwroc() {
	sStudent s1;
	s1.pobierz();
	return s1;
}

a to header.h

#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;

struct sStudent {
public:
	//atrubuty
	string imie;
	string nazwisko;
	int rok_urodzenia;
	vector<pair<string, float>> oceny;


	//metody
	void pobierz();
	void pobierz_oceny(const vector<sStudent> &studenci);
	void wyswietl();
	void wyswietl_oceny();
	sStudent zwroc();

};
int menu();

1

Nie jestem pewien co Kombinujesz, ale wydaje mi się, że Chcesz podając studenta i nazwisko, dostać jego oceny, to chyba powinno być prosto:

 std::vector<pair<string, float>> student::get_grade(const std::vector<student>& students){
	 // somehow get to_find here...
	for(auto & s : students){
		if (s.nazwisko == to_find)
			return s.oceny;
	}
	return /* Default, exception?*/
}

Wysyłając strukturę, jako stałą referencję można, jeśli jest żle zaprojektowana (ma "mutable" pola).

2

@lion137: w mojej opinii, idealnym rozwiązaniem jest zwracanie std::optional<std::vector<pair<string, float>>>. Wówczas oczywistym jest, co się zwróci, gdy nie będzie zadanego studenta itd.

0

Chcę dodać oceny do wyszukanego po nazwisku studenta. Użytkownikiem w tym programie ma być nauczyciel który dodaje studentów w case 1 i potem case 2 dodaje do studentów( z wektora) znalezionych po wpisanym nazwisku oceny z poszczególnych przedmiotów np ("MATEMATYKA" , 4.0). Zadaniem późniejszych casów będzie dopiero wyświetlanie i sortowanie uczniów po imieniu, nazwisku lub ocenach albo obliczanie średniej dla ucznia z poszczególnych przedmiotów (z ocen o tych samych nazwach) . Ale to dopiero potem... na razie chcę przypisać oceny do wektora a nie je odczytać.

0

To podobna funkcja, jak CI podałem, różniąca się tylko: jest void, poza referencją na wektor studenta musi pobierać oceny, a w linii:
return.s;,
będzie jakoś ustawiać oceny znalezionego studenta, np.:
s.set_grades(grades /* pobrane jako parametr */);.

2
Pyronis napisał(a):
		case 2: {

			cout << "Podaj nazwisko studenta, ktorego oceny chcesz wprowadzic" << endl;
			string nazwisko;
			cin.ignore();
			getline(cin, nazwisko);
			vector<sStudent>::iterator itr;

			itr = find(studenci.begin(), studenci.end(), nazwisko); //  tutaj jest blad

Mamy 2018, korzystaj z C++11 (lub nowszy):

auto itr = std::find_if(studenci.begin(), studenci.end(),
        [nazwisko](const auto &student) { // C++14, dla C++11 zamiast "auto" ma być "sStudent"
            return nazwisko == student.nazwisko;
        });
0

Ta odpowiedź bardzo mnie satysfakcjonuje i rozwiązuje mój problem w całości za co chciał bym serdecznie podziękować.
Mam jednak jeszcze jedno pytanie do predykatu funkcji find_if()

[nazwisko](const auto &student) {
            return nazwisko == student.nazwisko;
        }

(const auto &student) - argumenty jakie przekazujemy funkcji,
{return nazwisko == student.nazwisko;} - ciało funkcji, które zwraca true jeśli jest spełniony warunek nazwisko == student.nazwisko,
[nazwisko] - nie rozumiem co oznacza dokładnie ten fragment, czy to jest kolejny argument przekazywany do funkcji, bo predykat musi porównywać dwa stringi czyli jeden ze struktury przekazanej przez referencję i drugi czyli nazwisko wprowadzone przez użytkownika w 2 case?? I dlaczego nazwisko jest w nawiasach kwadratowych??
Do tej pory pisałem tylko takie predykaty mające 2 takie same argumenty do funkcji sort() :

bool po_nazwisku(sStudent s1, sStudent s2){
	if (s1.nazwisko < s2.nazwisko) return true;
	if (s1.nazwisko == s2.nazwisko) {
		if (s1.imie < s2.imie) return true;
		if (s1.imie == s1.imie)
			return s1.rok_urodzenia < s2.rok_urodzenia;
	}
	return false;
}
sort(studenci.begin(), studenci.end(), po_nazwisku);

Może przedstawienie tego w postaci funkcji bool będzie łatwiejsze dla mnie do zrozumienia.

3

Doczytaj czym są lambdy w C++11.
W tym wypadku lambda nie definiuje po prostu funkcję, ale obiekt funkcyjny. Masz funkcję która jest sparametryzowana zmienną nazwisko i ten obiekt funkcyjny trzyma tą informację w sobie.

W C++03 wygląda to tak:

class MatchesLastName : public std::unary_function<sStudent, bool>
{
public:
    MatchesLastName(const std::string& lastName)
        : mLastName(lastName)
    {}

    ResultType operator()(const ArgumentType& student) const
    {
        return mLastName == student.nazwisko;
    }

private:
    const std::string mLastName;
};
....

std::vector<sStudent>::iterator itr = std::find_if(studenci.begin(), studenci.end(), MatchesLastName(nazwisko));

Jak widzisz trzeba napisać troszkę "boiler plate code", a robi to dokładnie to samo.
W C++03 są szablony które troszkę pomagają, ale są nieporęczne. W C++03 tr1 (technical review one) jest jeszcze std::tr1::bind, które jest przyjemniejsze w użyciu (w C++11 jest to po prostu std::bind, ale jako, że masz tam też lambdy to nie ma sensu się z tym męczyć).

0

Bardzo dziękuję za pomoc :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1