konstruktor klasy pochodnej z argumentem będącym referencją do obiektu klasy abstrakcyjnej

0

Cześć! Przerabiam książkę "Język C++. Szkoła programowania" Stephana Prata. Jestem na rozdziale mówiącym o dziedziczeniu, itp. Mam pytanie odnośnie konstruktora klasy pochodnej.
Jak wiadomo nie można tworzyć obiektów klasy abstrakcyjnej. W przykładzie występuje konstruktor klasy pochodnej, który jako jeden z argumentów ma referencję do obiektu klasy bazowej abstrakcyjnej.
Skoro nie mogę przekazać mu obiektu klasy abstrakcyjnej (bo taki nie może powstać) to czy mogę mu przekazać obiekt klasy pochodnej, który zawiera podobiekt klasy bazowej?
Czy taki jest sens takiego konstruktora? Oto plik nagłówkowy z interfejsem (pytanie o m.in. linijkę 59):

// workermi.h -- hierarchia klas z uzyciem dziedziczenia wielokrotnego
#ifndef WORKERMI_H_
#define WORKERMI_H_

#include <string>

class Worker // abstrakcyjna klasa bazowa
{
private:
	std::string fullname;
	long id;
protected:
	virtual void Data() const;
	virtual void Get();
public:
	Worker() : fullname("brak"), id(0L) {}
	Worker(const std::string & s, long n)
		: fullname(s), id(n) {}
	virtual ~Worker() = 0; // funkcja czysto wirtualna
	virtual void Set() = 0;
	virtual void Show() const = 0;
};

class Waiter : virtual public Worker
{
private:
	int panache;
protected:
	void Data() const;
	void Get();
public:
	Waiter() : Worker(), panache(0) {}
	Waiter(const std::string & s, long n, int p = 0)
		: Worker(s, n), panache(p) {}
	Waiter(const Worker & wk, int p = 0)
		: Worker(wk), panache(p) {}
	void Set();
	void Show() const;
};

class Singer : virtual public Worker
{
protected:
	enum {inna, alt, kontralt, sporan,
		  bas, baryton, tenor};
	enum {Vtypes = 7};
	void Data() const;
	void Get();
private:
	static const char *pv[Vtypes]; // odpowiednik skali glosu w postaci ciagu znakow
	int voice;
public:
	Singer() : Worker(), voice(inna) {}
	Singer(const std::string & s, long n, int v = inna)
		: Worker(s, n), voice(v) {}
//////////////////////////////////////		
// PYTANIE ODNOSCIE NP. TEGO FRAGMENTU
//////////////////////////////////////
	Singer(const Worker & wk, int v = inna)
		: Worker(wk), voice(v) {}
	void Set();
	void Show() const;
};

// dziedziczenie wielokrotne
class SingingWaiter : public Singer, public Waiter
{
protected:
	void Data() const;
	void Get();
public:
	SingingWaiter() {}
	SingingWaiter(const std::string & s, long n, int p = 0,
				  int v = inna)
		: Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
	SingingWaiter(const Worker & wk, int p = 0, int v = inna)
		: Worker(wk), Waiter(wk, p), Singer(wk, v) {}
	SingingWaiter(const Waiter & wt, int v = inna)
		: Worker(wt), Waiter(wt), Singer(wt, v) {}
	SingingWaiter(const Singer & wt, int p = 0)
		: Worker(wt), Waiter(wt, p), Singer(wt) {}
	void Set();
	void Show() const;
};

#endif // WORKERMI_H_

1

Po prostu wywołujesz bezpośrednio konstruktor kopiujący Worker. Więc możesz. Tylko inna sprawa jest taka, że dziedziczenia wirtualnego właściwie nie należy używać - jeśli uważasz że jest ono rozwiązaniem Twojego problemu to poważnie zastanów się nad architekturą swojego kodu.

0
kq napisał(a):

Po prostu wywołujesz bezpośrednio konstruktor kopiujący Worker. Więc możesz.

Czyli w wywołaniu konstruktora muszę podać jakiś obiekt Worker, który chce skopiować do obiektu Worker. Rozumiem, że taki jest sens konstruktora kopiującego, ale skąd wezmę taki obiekt, skoro to klasa abstrakcyjna? Proszę o wyrozumiałość ;)

1
haracz napisał(a):
kq napisał(a):

Po prostu wywołujesz bezpośrednio konstruktor kopiujący Worker. Więc możesz.

Czyli w wywołaniu konstruktora muszę podać jakiś obiekt Worker, który chce skopiować do obiektu Worker. Rozumiem, że taki jest sens konstruktora kopiującego, ale skąd wezmę taki obiekt, skoro to klasa abstrakcyjna? Proszę o wyrozumiałość ;)

Podstawy dziedziczenia. Książka o tym nie wspomina? Obiekt klasy pochodnej jest także obiektem klasy bazowej. Widać w klasie SingingWaiter, że przyjmuje obiekty typy Waiter i wywołuje konstruktor klasy Singer przekazując mu obiekt typu Waiter, który z powodu dziedziczenia jest także typu Worker.

I na koniec uwaga praktyczna - taki kod nie ma sensu. Eleganckim rozwiązaniem dla podobnych problemów jest kompozycja. C++ co prawda ma mechanizm dziedziczenia wirtualnego, ale prawie na pewno użycie tego mechanizmu to poważny błąd projektowy.

0

Obiekt klasy pochodnej jest także obiektem klasy bazowej. Widać w klasie SingingWaiter, że przyjmuje obiekty typy Waiter i wywołuje konstruktor klasy Singer przekazując mu obiekt typu Waiter, który z powodu dziedziczenia jest także typu Worker

Właśnie o tym pisałem w pierwszym poście, o kopiowaniu podobiektu klasy pochodnej, który jest obiektem klasy bazowej.

Ok, kiedy przekazuje konstruktorowi klasy Singer obiekt typu Waiter to rozumiem, ze kopiowany jest tylko podobiekt Worker, ktory jest klasą bazową.
Do czego zmierzam... Nie wiem czy to nie prowadzi do wieloznacznosci, gdyz:

SingingWaiter(const Worker & wk, int p = 0, int v = inna)
        : Worker(wk), Waiter(wk, p), Singer(wk, v) {}
SingingWaiter(const Waiter & wt, int v = inna)
        : Worker(wt), Waiter(wt), Singer(wt, v) {}
SingingWaiter(const Singer & wt, int p = 0)
        : Worker(wt), Waiter(wt, p), Singer(wt) {}

Pierwszy konstruktor spodziewa sie obiektu klasy pochodnej, ktora zawiera podobiekt klasy Worker, a więc Waiter lub Singer.
Drugi konstruktor podobnie jak pierwszy spodziewa sie obiektu klasy pochodnej Waiter lub Singer w zwiazku z tym co zostało napisane wyżej.
Trzeci konstruktor również spodziewa się obiektu klasy pochodnej Singer lub Waiter.

Prosze wyprowadźcie mnie z błędu jeśli się mylę.

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