Wywołanie wirtualnych funkcji klasy dziedziczących na podstawie wskaźników klasy bazowej w vectorze

0

Cześć mam problem z vectorem przechowujący wskaźniki shared_ptr klasy bazowej. Chce przez analogie do wskaźników surowych wywołać odpowiednie metody klas pochodnych, ale nie wiem jak sobie poradzić. Próbowałem z static_cast albo dynamic_pointer_cast ale nie działa.

Dzięki za pomoc ;)

header.h

#ifndef ACCOUNT_H_
#define ACCOUNT_H_
#include <iostream>
#include <string>
#include <memory>

// abstrakcyjna klasa bazowa
class AcctABC
{
public:
	AcctABC(const std::string & s = "brak", long an = -1, double bal = 0.0);
	void Deposit(double amt);
	virtual void Withdraw(double amt) = 0; 
	double Balance() const { return balance; }
	virtual void ViewAcct() const = 0;
	virtual ~AcctABC() {}
private:
	std::string fullName;
	long acctNum;
	double balance;

protected:
	struct Formatting
	{
		std::ios_base::fmtflags flag;
		std::streamsize pr;
	};
	const std::string & FullName() const { return fullName; }
	long AcctNum() const { return acctNum; }
	Formatting SetFormat() const;
	void Restore(Formatting & f) const;
};

// klasa do obsługi rachunku Brass
class Brass : public AcctABC
{
public:
	Brass(const std::string & s = "brak", long an = -1, double bal = 0.0) : 
		AcctABC(s, an, bal) 
	{}
	virtual void Withdraw(double amt) override;
	virtual void ViewAcct() const override;
	virtual ~Brass() {}
};

// klasa do obsługi rachunku BrassPlus
class BrassPlus : public AcctABC
{
private:
	std::shared_ptr<BrassPlus> shptr;
	double maxLoan;
	double rate;
	double owesBank;
public:
	BrassPlus(const std::string & s = "brak", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10);
	BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
	virtual void ViewAcct() const override;
	virtual void Withdraw(double amt) override;
	void ResetMax(double m) { maxLoan = m; }
	void ResetRate(double r) { rate = r; }
	void ResetOwes() { owesBank = 0; }
};

#endif

def.cpp

#include <iostream>
#include "header.h"
using std::cout;
using std::ios_base;
using std::endl;

using std::string;

// abstrakcyjna klasa bazowa
AcctABC::AcctABC(const string & s, long an, double bal) :
	fullName(s),
	acctNum(an),
	balance(bal)
{
}

void AcctABC::Deposit(double amt)
{
	if (amt < 0)
		cout << "Nie możesz wpłacić ujemnej kwoty; "
		<< "Wpłata anulowana.\n";
	else
		balance += amt;
}

void AcctABC::Withdraw(double amt)
{
	balance -= amt;
}

// metody chronione, zajmujące się formatowaniem
AcctABC::Formatting AcctABC::SetFormat() const
{
	// ustawia format w postaci ###.##
	Formatting f;
	f.flag =
		cout.setf(ios_base::fixed, ios_base::floatfield);
	f.pr = cout.precision(2);
	return f;
}

void AcctABC::Restore(Formatting & f) const
{
	cout.setf(f.flag, ios_base::floatfield);
	cout.precision(f.pr);
}

// metody klasy Brass
void Brass::Withdraw(double amt)
{
	if (amt < 0)
		cout << "Nie można wypłacić ujemnej kwoty; "
		<< "Wypłata anulowana.\n";
	else if (amt <= Balance())
		AcctABC::Withdraw(amt);
	else
		cout << "Żądana wartość " << amt
		<< " zł przekracza dostępne środki.\n"
		<< "Wypłata anulowana.\n";
}

void Brass::ViewAcct() const
{
	Formatting f = SetFormat();
	cout << "Właściciel rachunku Brass: " << FullName() << endl;
	cout << "Numer rachunku: " << AcctNum() << endl;
	cout << "Stan konta: " << Balance() << " zł" << endl;
	Restore(f);
}

// metody klasy BrassPlus
BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : 
	AcctABC(s, an, bal),
	maxLoan(ml),
	owesBank(0.0),
	rate(r)
{}

BrassPlus::BrassPlus(const Brass & ba, double ml, double r) :
	AcctABC(ba), // używa niejawnego konstruktora kopiującego
	maxLoan(ml),
	owesBank(0.0),
	rate(r)
{}

void BrassPlus::ViewAcct() const
{
	Formatting f = SetFormat();
	cout << "Właściciel rachunku BrassPlus: " << FullName() << endl;
	cout << "Numer rachunku: " << AcctNum() << endl;
	cout << "Stan konta: " << Balance() << " zł" << endl;
	cout << "Limit debetu: " << maxLoan << " zł" << endl;
	cout << "Kwota zadłużenia: " << owesBank << " zł" << endl;
	cout.precision(3);
	cout << "Stopa oprocentowania: " << 100 * rate << "%\n";
	Restore(f);
}

void BrassPlus::Withdraw(double amt)
{
	Formatting f = SetFormat();

	double bal = Balance();
	if (amt <= bal)
		AcctABC::Withdraw(amt);
	else if (amt <= bal + maxLoan - owesBank)
	{
		double advance = amt - bal;
		owesBank += advance * (1.0 + rate);
		cout << "Zadłużenie faktyczne: " << advance << " zł" << endl;
		cout << "Odsetki: " << advance * rate << " zł" << endl;
		Deposit(advance);
		AcctABC::Withdraw(amt);
	}
	else
		cout << "Przekroczony limit debetu. Operacja anulowana.\n";
	Restore(f);
}

test.cpp

#include "header.h"
#include <vector>
#include <algorithm>

const int CLIENTS = 2;

int main()
{
	using std::cin;
	using std::cout;
	using std::endl;
	using std::vector;
	std::string temp;
	long tempnum;
	double tempbal;
	char kind;
	vector<std::shared_ptr<AcctABC>> pClientVector;
	for (int i = 0; i < CLIENTS; i++)
	{
		cout << "Podaj imię i nazwisko klienta: ";
		getline(cin, temp);
		cout << "Podaj numer rachunku klienta: ";
		cin >> tempnum;
		cout << "Podaj początkowy stan konta: $";
		cin >> tempbal;
		cout << "Wpisz 1 dla rachunku Brass lub "
			<< "2 dla rachunku BrassPlus: ";
		while (cin >> kind && (kind !='1' && kind != '2'))
			cout << "Wpisz 1 lub 2: ";
		if(kind == '1')
		{
			auto p_client1(std::make_shared<Brass>(temp, tempnum, tempbal));
			pClientVector.push_back(p_client1);
		}
		else if(kind =='2')
		{
			double tmax, trate;
			cout << "Podaj limit debetu: ";
			cin >> tmax;
			cin.get();
			cout << "Podaj stopę oprocentowania "
				<< "w postaci ułamka dziesiętnego: ";
			cin >> trate;
			auto p_client2(std::make_shared<BrassPlus>(temp, tempnum, tempbal, tmax, trate));
			pClientVector.push_back(p_client2);
		}
		while (cin.get() != '\n')
			continue;
	}
	cout << endl;

	for(auto p=pClientVector.begin(); p != pClientVector.end(); p++)
	{
		auto brassPointer = std::make_shared<Brass>();
		auto brassPlusPointer= std::make_shared<BrassPlus>();
		if (brassPointer = dynamic_cast<Brass>(p))
		{

		}
	}
	
	//for (int i = 0; i < CLIENTS; i++)    //Jeżeli zastosujemy surowe wskaźniki 
	//{
	//	p_clients[i]->ViewAcct();
	//	cout << endl;
	//}
	
	cout << "Gotowe.\n";
	return 0;
}

Link do kompilatora online z kodem wstawionym:
https://onlinegdb.com/ryVjcnF_z

0

Przeważnie jak robisz rzutowanie na klasę pochodną to znaczy tyle że masz błąd w projekcie. Przedstaw problem tekstem to zastanowimy się.

0

Chciałem przewalić projekt na nowszą wersję:

class AcctABC:
{
public:
....
virtual void ViewAcct() const = 0;
....
}

class Brass : public AcctABC
{
public:
....
virtual void ViewAcct() const override;
....
}

class BrassPlus : public AcctABC
{
public:
....
virtual void ViewAcct() const override;
....
}
BAZOWA * pBase[CLIENT];
...
for( int i=0;.... i++)
if(warunek1)
{
pBase[i]=new POCHODNA1(argumenty) // konstruktor klasy pochodnej
}
else if(warunek2)
{
pBase[i]=new POCHODNA2(argumenty) // konstruktor klasy pochodnej
}

for (int i = 0; i < CLIENTS; i++)    //Jeżeli zastosujemy surowe wskaźniki 
    {
      p_clients[i]->ViewAcct(); // ViewAcct() to abstrakcyjna metoda dla klasy bazowej, 
      cout << endl;
    }

Dzięki temu wskaźniki klasy bazowej wskazują na adresy obiektów klasy pochodnych. Bez problemu byłem w stanie wywołać metody klas pochodnych (dzięki wiązaniu dynamicznemu wskaźnika który wskazywał na odpowiednie klasy dziedziczące).

Gdy chciałem podmienić wskaźnik surowy na inteligentny shared_ptr, projekt się rozsypał:

vector<std::shared_ptr<BAZOWA>> pBASE;
...
for( int i=0;.... i++)
if(warunek1)
{
        auto wskaźnikPochodna1(std::make_shared<Pochodna1>(argumenty));
	pClientVector.push_back(wskaźnikPochodna1);
}
else if(warunek2)
{
        auto wskaźnikPochodna2(std::make_shared<Pochodna2>(argumenty));
	pClientVector.push_back(wskaźnikPochodna2);
}

for(auto p=pClientVector.begin(); p != pClientVector.end(); p++)
{
            p->ViewAcct(); // błąd
}

//Próbowałem też coś takiego:
for(auto p=pClientVector.begin(); p != pClientVector.end(); p++)
    {
        auto brassPointer = std::make_shared<Brass>();
        auto brassPlusPointer= std::make_shared<BrassPlus>();
        if (brassPointer = dynamic_cast<Brass>(p))
        {

        }
    }

Gdyby coś jeszcze nie było jasne to postaram pokazać co miałem na myśli ;)

1
auto wskaźnikPochodna1(std::make_shared<Pochodna1>(argumenty));
    pClientVector.push_back(wskaźnikPochodna1);

Takie coś jest nie czytelne. Nie lepiej od razu make_shared i emplace_back?

if(warunek1)
{

Nie lepiej mapa w połączeniu z wzorcem fabryki.

for(auto p=pClientVector.begin(); p != pClientVector.end(); p++)

użyj for_each w c++ np. for (auto obj : objs)

 p->ViewAcct(); // błąd

tak jest jak się wali bez zastanownienia auto, czym jest p?

0
Maciej_ napisał(a):

for(auto p=pClientVector.begin(); p != pClientVector.end(); p++)
{
auto brassPointer = std::make_shared<Brass>();
auto brassPlusPointer= std::make_shared<BrassPlus>();
if (brassPointer = dynamic_cast<Brass>(p))
{

Przede wszystkim napisz co chcesz osiągnąć, bo póki co naklepałeś trochę kodu a w kluczowym momencie... zostawiłeś puste linie i tak naprawdę nie wiadomo po co Ci to rzutowanie na klasę pochodną potrzbne.

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