Pobieranie pól klasy pochodnej poprzez klasę bazową - Polimorfizm

0

Witam.
W książce A. Allaina "Przewodnik dla początkujących C++" jest takie zadanie:

Zaimplementuj funkcję sortuj pobierającą wektor wskaźników do klasy interfejsowej „Porownywalne”, która definiuje metodę „porownaj(Porownywalne& inny)” i zwraca 0, kiedy obiekty są takie same, 1, kiedy porównywany obiekt jest większy od obiektu „inny”, oraz -1, gdy obiekt jest mniejszy od obiektu „inny”. Napisz klasę implementującą ten interfejs, utwórz kilka jej instancji i posortuj je.

I zrobiłem to w ten sposób ( jakość kodu ma mniejsze znaczenie)

class Interface
{
	public:
		virtual int compare(Interface&) = 0;
		virtual int getValue() = 0;
};

class Sort : public Interface
{
	int value;
	public:
		int getValue() { return value; }
		int compare(Interface& another) override
		{
			if(this->getValue() > another->getValue()) return 1;
			else if(this->getValue() < another->getValue()) return -1;
			else return 0;
		}
		Sort(int n) : value{n} {}
};

void sort(std::vector<Interface*>& numbers)
{
	for(int i = 0; i < numbers.size(); ++i)
	{
		for(int j = 0; j < numbers.size() - 1; ++j)
		{
			
			if(numbers[j]->compare(numbers[j + 1]) == 1)
			{
				std::swap(numbers[j], numbers[j + 1]);
			}
		}
	}
}

W związku z tym mam takie pytanie. Czy ja powinienem do pól klasy pochodnej dostawać się z wirtualnych metod klasy bazowej,
czy może ten obiekt castować w dół i bezpośrednio
już z klasy pochodnej pobierać?
No chyba, że obydwa pomysły są złe, to prosiłbym o poprawne nakierowanie na dobre rozwiązanie.
Dzięki za odpowiedz i pozdrawiam.

0

Po to istnieją interface aby nie robić żadnych kastowań

0

Nie prościej tak:

int compare(Interface& another) override
{
    return value - another.getValue();
}

?

W zasadzie compare i getValue powinny być const.

0

Zamień sobie another->getValue() na another.getValue() gdyż operujesz na referencjach.
Odnośnie interfejsu to powinien zawierać wirtualny destruktor, tak aby uniknąć grożącego UB w przypadku usuwania obiektów za pomocą wskaźnika do klasy Interface.

class Interface
{
    public:
        virtual ~Interface() {}
        virtual int compare(Interface&) const noexcept = 0;
        virtual int getValue() const noexcept = 0;
};
2

Zaimplementuj funkcję sortuj pobierającą wektor wskaźników do klasy interfejsowej „Porownywalne”,
która definiuje metodę „porownaj(Porownywalne& inny)” i zwraca 0, kiedy obiekty są takie same, 1, kiedy porównywany obiekt jest większy od obiektu „inny”, oraz -1, gdy obiekt jest mniejszy od obiektu „inny”.

class Compareable
{
public:
     ~Compareable() {}
     virtual int compare(const Compareable& other) const = 0;
};

void mySort(std::vector<Compareable *> &v)
{
     std::sort(v.begin(), v.end(), [](const auto &a, const auto &b) { return a->compare(*b) < 0; });
}

Napisz klasę implementującą ten interfejs, utwórz kilka jej instancji i posortuj je.

class Student : public Compareable
{
public:
     Student(std::string name) : lastName{ std::move(name) }
     {}

     int compare(const Student& other) const
     {
         return lastName.compare(other.lastName);
     }

     int compare(const Compareable& other) const override
     {
          return compare(dynamic_cast<const Student&>(other));
     }

private:
    std::string lastName;
};

Przy czym nie ma domyślnej konwersji między std::vector<Pochodna *> do std::<Bazowa *>, więc trzeba zdecydować się na jakiś kompromis.

0

Zamiast używać rzutowania w klasie pochodnej można zastosować Curiously recurring template pattern

using namespace std;

template<typename T>
class Compareable
{
public:
    virtual int compare( T const& other ) const = 0;
};

class Student : public Compareable<Student>
{
public:
    Student( string name ) : lastName {move(name)} {}

    int compare( Student const& other ) const { return lastName.compare(other.lastName); }

private:
    string lastName;
};
0
TomaszLiMoon napisał(a):

Zamiast używać rzutowania w klasie pochodnej można zastosować Curiously recurring template pattern

Raczej problemem jest mySort i prościej (bez mącenia w głowie początkującemu z CRTP), użyć szablonu i "duck typing":

template<typename T>
void mySort(std::vector<T> &v)
{
     std::sort(v.begin(), v.end(), [](const auto &a, const auto &b) { return a->compare(*b) < 0; });
}

Lub bardziej szpanerskie: https://wandbox.org/permlink/I6kINtsetGrM8jfD

Wszystko zależy od tego, co właściwie ma trzymać ten vector i na czym nam zależy.

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