I have a base class for pieces
class piece;
and an array containing derived objects
piece* board[8][8];
Advantag
You can't worry about performance and code for fun at the same time :)
Consider having "nibbleboard" (or at least byteboard) instead of bitboard, where each nibble represents one piece type. Each nibble is also index in the table of singleton objects that operate on that piece type.
class Empty : public Piece {};
class Rook : public Piece {};
...
const int wrook = 1;
...
const int bpawn = 12;
Piece* Operator[13] = {new Empty(), new Rook(), ..., new Pawn()};
byte table[64] = {
wrook, wbishop, wknight, wking, wqueen, wknight, wbishop, wrook,
wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn, wpawn,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn, bpawn,
brook, bbishop, bknight, bking, bqueen, bknight, bbishop, brook};
// Given some position and some operation DoSomething we would have this:
Operator[table[position]]->DoSomething(table, position, );
// Possible return value of DoSomething might be new table