PDA

Visualizza la versione completa : [C++] Ereditarietà e funzioni virtual


superamble
16-02-2012, 13:56
Sto realizzando un programma sul gioco degli scacchi, per rappresentare e movimentare le pedine ho realizzato una classe base ChessPiece e 8 classi derivate, una per ogni tipologia di pezzo(pedoni,re,regina,etc...). La classe base ha un metodo move che è virtual, ho realizzato quindi un metodo move con la stessa signature anche per le derivate.
La scacchiera è una matrice di puntatori a ChessPiece e alla creazione vengono opportunamente richiamati i giusti costruttori in base al tipo di pezzo (verificato).
Applico quindi il metodo move su ogni puntatore, però invece di richiamare i metodi delle classi derivate chiama quello della classe base.

Non so se può influire : le classi Torre e Alfiere sono virtual in quanto da esse deriva la classe Regina, questa scelta è dettata dal fatto che la regina è in grado di muoversi sia come un alfiere che come una torre.

Non so che parti di codice postare.

superamble
16-02-2012, 14:35
Ho capito dov'è l'errore, ma so spiegarvelo solo a livello sintattico non capisco bene il motivo e non so risolverlo.
Questa è la chiamata al metodo move:
game[i][j].move(i,j,game);

game è la matrice dei puntatori che ho realizzato così

ChessPiece ** Game(char ** cGame) { ChessPiece ** game; game=new ChessPiece *[8]; for(int i=0;i<=7;i++) { game[i]=new ChessPiece[8]; for(int j=0;j<=7;j++) { char piece=cGame[i][j]; switch(piece) { case 'k': { game[i][j]= King(piece); break; } case 'K': { game[i][j]= King(piece); break; } case 'q': { game[i][j]= Queen(piece); break; } case 'Q': { game[i][j]= Queen(piece); break; } case 'b': { game[i][j]= Bishop(piece); break; } case 'B': { game[i][j]= Bishop(piece); break; } case 'r': { game[i][j]= Rook(piece); break; } case 'R': { game[i][j]= Rook(piece); break; } case 'n': { game[i][j]= Knight(piece); break; } case 'N': { game[i][j]= Knight(piece); break; } case 'p': { game[i][j]= Pawn(piece); break; } case 'P': { game[i][j]= Pawn(piece); break; } case '.': { game[i][j]= ChessPiece(); break; } } } } return game; }

L'errore è che applico move sull'oggetto e non sul puntatore vero?

ramy89
16-02-2012, 14:41
Posta il codice completo e non tutto su una riga, copia e incolla l' errore che ti da.
Al limite se è kilometrico posta le parte più importanti separatamente dal codice completo.

superamble
16-02-2012, 15:54
ChessPiece ** Game(char ** cGame)
{
ChessPiece ** game;
game=new ChessPiece *[8];
for(int i=0;i<=7;i++)
{
game[i]=new ChessPiece[8];
for(int j=0;j<=7;j++)
{
char piece=cGame[i][j];
switch(piece)
{
case 'k':
{
game[i][j]= King(piece);
break;
}
case 'K':
{
game[i][j]= King(piece);
break;
}
case 'q':
{
game[i][j]= Queen(piece);
break;
}
case 'Q':
{
game[i][j]= Queen(piece);
break;
}
case 'b':
{
game[i][j]= Bishop(piece);
break;
}
case 'B':
{
game[i][j]= Bishop(piece);
break;
}
case 'r':
{
game[i][j]= Rook(piece);
break;
}
case 'R':
{
game[i][j]= Rook(piece);
break;
}
case 'n':
{
game[i][j]= Knight(piece);
break;
}
case 'N':
{
game[i][j]= Knight(piece);
break;
}
case 'p':
{
game[i][j]= Pawn(piece);
break;
}
case 'P':
{
game[i][j]= Pawn(piece);
break;
}
case '.':
{
game[i][j]= ChessPiece();
break;
}
}
}
}
return game;
}


Questa è la chiamata al metodo move:
game[i][j].move(i,j,game);

ramy89
16-02-2012, 16:13
Postalo tutto il codice,compreso quello del main.Sennò come faccio a intuire cosa stai tentando di fare?

superamble
16-02-2012, 16:28
#include <iostream>
using namespace std;
class ChessPiece
{

public:
ChessPiece(){color=-2;type=' ';}
int color; //0 black 1white
char type;//p:pawn b:bishop r:Rook q:queen k:king n:knight
virtual int move(int i,int j,ChessPiece ** game){cout<<endl<<"No Piece moving"<<endl; return -1; }
};

class King : public ChessPiece
{
public:
King::King(char k);
int move(int i,int j,ChessPiece ** game);
};

class Pawn : public ChessPiece
{
public:
Pawn::Pawn(char p);
virtual int move(int i,int j,ChessPiece ** game);

};

class Rook : virtual public ChessPiece
{

public:
Rook::Rook(){}
Rook::Rook(char r);
virtual int move(int i,int j,ChessPiece ** game);

};

class Bishop : virtual public ChessPiece
{
public:
Bishop::Bishop(){}
Bishop::Bishop(char b);
virtual int move(int i,int j,ChessPiece ** game);

};

class Knight : public ChessPiece
{
public:
Knight::Knight(char n);
virtual int move(int i,int j,ChessPiece ** game);

};

class Queen : public Rook, public Bishop
{
public:
Queen::Queen(char p);
int move(int i,int j,ChessPiece ** game);

};

ChessPiece ** Game(char ** cGame);

int check(ChessPiece ** game);

#include "header.h"
#include <string>
using namespace std;

ChessPiece ** Game(char ** cGame)
{
ChessPiece ** game;
game=new ChessPiece *[8];
for(int i=0;i<=7;i++)
{
game[i]=new ChessPiece[8];
for(int j=0;j<=7;j++)
{
char piece=cGame[i][j];
switch(piece)
{
case 'k':
{
game[i][j]= King(piece);
break;
}
case 'K':
{
game[i][j]= King(piece);
break;
}
case 'q':
{
game[i][j]= Queen(piece);
break;
}
case 'Q':
{
game[i][j]= Queen(piece);
break;
}
case 'b':
{
game[i][j]= Bishop(piece);
break;
}
case 'B':
{
game[i][j]= Bishop(piece);
break;
}
case 'r':
{
game[i][j]= Rook(piece);
break;
}
case 'R':
{
game[i][j]= Rook(piece);
break;
}
case 'n':
{
game[i][j]= Knight(piece);
break;
}
case 'N':
{
game[i][j]= Knight(piece);
break;
}
case 'p':
{
game[i][j]= Pawn(piece);
break;
}
case 'P':
{
game[i][j]= Pawn(piece);
break;
}
case '.':
{
game[i][j]= ChessPiece();
break;
}
}
}
}
return game;
}


int check(ChessPiece ** game)
{
int loser=-1;
for(int i=0;i<=7 && loser==-1;i++)
{
for(int j=0;j<=7 && loser==-1;j++)
{
if(game[i][j].color!=-2)
loser=game[i][j].move(i,j,game);
}
}

return loser;
}

#include "stdafx.h"
#include "header.h"
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
char ** cGame ;
string s1="..k.....ppp.pppp.........R...B..................PP PPPPPPK.......";
int i=0;
cGame=new char *[8];
for (int w=0;w<=7;w++)
{
cGame[w]=new char[8];
for(int k=0;k<=7;k++)
{
cGame[w][k]=s1[i];
cout<<cGame[w][k];
i++;
}
cout<<endl;
}
i=0;
cout<<endl;

ChessPiece ** game=Game(cGame);
for (int w=0;w<=7;w++)
{
for(int k=0;k<=7;k++)
{

cout<<"("<<game[w][k].type<<","<<game[w][k].color<<")";
i++;
}
cout<<endl;
}

int loser=check(game);

switch(loser)
{
case -1:{cout<<"Game #d: no king is in check."<<endl; break;}
case 0:{cout<<"Game #d: black king is in check."<<endl; break;}
case 1:{cout<<"Game #d: white king is in check."<<endl; break;}
default :{cout<<"prob"<<endl; break;}
}
cout<<loser<<endl;

int a=0;
cin>>a;
return 0;
}



Credo che comunque, come ho scritto in precedenza , il problema stia nella prima porzione di codice che ho postato e in particolare sulla chiamata di move(). Ho fatto delle prove e scrivendo :

PieceChess * p=new King('k');
int loser=p.move(i,j,game);

funziona tutto.

Invece evidentemente nei metodi check() e game() faccio qualcosa di diverso, ma non capisco bene cosa.
Scrivendo


ChessPiece ** game;
game=new ChessPiece *[8];
for(int i=0;i<=7;i++)
{
game[i]=new ChessPiece[8];
for(int j=0;j<=7;j++)
{
char piece=cGame[i][j];
game[i][j]= King(piece);
break;
}
}
int loser=game[0][0].move(0,0,game);


Cosa cambia rispetto a :
PieceChess * p=new King('k');
int loser=p.move(i,j,game);

ramy89
16-02-2012, 18:08
Originariamente inviato da superamble
Credo che comunque, come ho scritto in precedenza , il problema stia nella prima porzione di codice che ho postato e in particolare sulla chiamata di move().


Il problema era nel metodo game:



int check(ChessPiece ** game)
{
int loser=-1;
for(int i=0;i<=7 && loser==-1;i++)
{
for(int j=0;j<=7 && loser==-1;j++)
{
if(game[i][j].color!=-2)
loser=game[i][j].move(i,j,game);
}
}

return loser;
}


Non stavi allocando memoria, ma ti sei corretto tu stesso.
Quando ci sono porzioni di codice che si chiamano a vicenda non puoi sapere con certezza qual'è la parte che causa il problema, ammenochè non sia una porzione di codice indipendente dalle altre.
Comunque _tmain non è c++ standard, è fatto dalla Microsoft e ho il serio sospetto che l' unico motivo sia quello di rendere i programmi non portabili su altre piattaforme.
La funzione main del c++ standard è:



int main(int argc, char** argv)

superamble
16-02-2012, 18:16
Potresti dirmi come correggere?
Comunque hai postato il metodo Check, confermi che l'errore è in game?

MItaly
16-02-2012, 18:42
Il problema è in questo genere di codice:


game[i][j]= King(piece);
game è un ChessPiece **, per cui quando scrivi game[i][j] ti trovi ad assegnare un oggetto di tipo King ad uno di tipo ChessPiece.
In C++ (per una combinazione del fatto che è un linguaggio a tipizzazione statica e con semantica degli oggetti by value) questo uccide il polimorfismo: l'oggetto di tipo King deve essere convertito in un oggetto di tipo ChessPiece, per cui viene invocato il costruttore di copie di ChessPiece che costruisce un ChessPiece sulla base dei dati dell'oggetto che gli viene passato, scartando i dati specifici di King (in particolare il suo tipo e il suo vptr); viene cioè effettuato il cosiddetto "slicing" (http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c).
Questo accade perché quando dichiari un oggetto (o un array di oggetti) il suo tipo è completamente fissato (altrimenti il compilatore non saprebbe quanto deve essere grande la variabile, quanta memoria allocare, ...).

Per preservare il polimorfismo devi memorizzare i tuoi oggetti per puntatore:


ChessPiece *** game;
// ...
game[i][k] = new King(piece);
// eccetera

questo funziona perché, nonostante il "tipo statico" del puntatore diventi ChessPiece * durante l'assegnamento, l'oggetto in sé viene lasciato in pace, per cui le chiamate ai metodi virtuali vengono risolte correttamente.
Nota però che usare tutti quei puntatori "liberi" in C++ non è una buona idea, dovresti usare una classe matrice e uno smart pointer per fare sì che i pezzi vengano deallocati correttamente.


Originariamente inviato da ramy89
Comunque _tmain non è c++ standard, è fatto dalla Microsoft e ho il serio sospetto che l' unico motivo sia quello di rendere i programmi non portabili su altre piattaforme.
Io invece ho il serio sospetto che tu non abbia idea del perché si usi _tmain e tutte le altre macro che iniziano per _t.

shodan
16-02-2012, 18:44
Originariamente inviato da ramy89
Comunque _tmain non è c++ standard, è fatto dalla Microsoft e ho il serio sospetto che l' unico motivo sia quello di rendere i programmi non portabili su altre piattaforme.


Il motivo è un altro, ben più storico e che riguarda UNICODE.
La serie Win9x si basava su stringhe ANSI, le versioni NT in poi su UNICODE.
Per evitare che un programmatore impazzisse a cambiare le chiamate API, in modo da adattarle alle varie stringhe, Microsoft ha creato un sistema di macro per astrarre le varie funzioni.
Giusto per fare un esempio, CreateWindow è definita così:


#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE

e così tutte le funzioni API che prendono stringhe.
Per la libc è leggermente diverso:


#ifdef _UNICODE
#define char TCHAR
#define strcpy _tcscpy
#else
#define wchar_t TCHAR
#define wcscpy _tcscpy
#endif // !_UNICODE

Loading