You are here:

C++/Copy Constructor for inherited class

Advertisement


Question
I need to know if my copy constructor for class Hand is correct, and what to do next in the class Deck to write correct copy constructor. Here is the program:

_________________________________________________
//Blackjack
//Plays a simple version of the casino game of
class Hand//HAND CLASS START
{
public:
   Hand();
   
   Hand(const Hand& c)// MAKE COPY CONSTRUCTOR
   {
       Copy (c);
   }
   
   Hand& Copy (const Hand& c);
   
   Hand& Hand::operator=(const Hand& rhs);
   
   virtual ~Hand();

   //adds a card to the hand
   void Add(Card* pCard);

   //clears hand of all cards
   void Clear();

   //gets hand total value, intelligently treats aces as 1 or 11
   int GetTotal() const;
protected:

   vector<Card*> m_Cards;//holds card pointers
//for memory on the heap
  
};


//COPY FUNCTION
Hand& Hand::Copy (const Hand& c)//COPY CONSTRUCTOR
{
    
   vector<Card*>::const_iterator iter = c.m_Cards.begin();
   
   for (iter = c.m_Cards.begin(); iter != c.m_Cards.end(); ++iter)
   {
       
       m_Cards.push_back(new Card);  //darn I am
   }
   
   return *this;
   
         
}
//END COPY FUNCTION
//START OVERLOAD OPERATOR
Hand& Hand::operator=(const Hand& rhs) //OPERATOR OVERLOADER
{
   if (&rhs!= this)
   {
       Clear();
       Copy(rhs);
       return *this;//returns the copy
   }

}

Hand::Hand()
{
   m_Cards.reserve(7);
}

Hand::~Hand()  //don't use the keyword virtual outside of class definition
{
   Clear();
}

void Hand::Add(Card* pCard)
{
   m_Cards.push_back(pCard);
}

void Hand::Clear()
{
   //iterate through vector, freeing all memory on the heap
   vector<Card*>::iterator iter = m_Cards.begin();
   for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
   {
       delete *iter;
       *iter = 0;
   }
   //clear vector of pointers
   m_Cards.clear();
}

int Hand::GetTotal() const
{
   //if no cards in hand, return 0
   if (m_Cards.empty())
       return 0;
 
   //if a first card has value of 0, then card is face down; return 0
   if (m_Cards[0]->GetValue() == 0)
       return 0;
   
   //add up card values, treat each Ace as 1
   int total = 0;
   vector<Card*>::const_iterator iter;
   for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
       total += (*iter)->GetValue();
         
   //determine if hand contains an Ace
   bool containsAce = false;
   for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
       if ((*iter)->GetValue() == Card::ACE)
         containsAce = true;
         
   //if hand contains Ace and total is low enough, treat Ace as 11
   if (containsAce && total <= 11)
       //add only 10 since we've already added 1 for the Ace
       total += 10;   
         
   return total;
}

class Deck : public Hand
{
public:
   Deck():rePopulate(9)//default constructor
   {
   m_Cards.reserve(52);
   Populate();
   }
   
   Deck(const Deck& c):Hand(c){};//copy constructor
   
   Deck& Deck::operator=(const Deck& oRhs );
    
   virtual ~Deck();

   //create a standard deck of 52 cards
   void Populate();

   //shuffle cards
   void Shuffle();

   //deal one card to a hand
   void Deal(Hand& aHand);

   //give additional cards to a generic player
   void AdditionalCards(GenericPlayer& aGenericPlayer);
protected:
int rePopulate;
};



Deck::~Deck()
{}


//OVERLOAD OPERATOR START
Deck& Deck::operator=(const Deck& oRhs )
{
   Hand::operator=(oRhs);
}

void Deck::Populate()
{
   Clear();
   //create standard deck
   for (int s = Card::CLUBS; s <= Card::SPADES; ++s)
         for (int r = Card::ACE; r <= Card::KING; ++r)
         Add(new Card(static_cast<Card::rank>(r), static_cast<Card::suit>(s)));
}

void Deck::Shuffle()
{
   random_shuffle(m_Cards.begin(), m_Cards.end());
}

void Deck::Deal(Hand& aHand)
{
   if (!m_Cards.empty())
   {
       aHand.Add(m_Cards.back());
       m_Cards.pop_back();
       
       int stackSize = m_Cards.size();//ASKING FOR THE VECTORS SIZE
       
       if (stackSize < rePopulate)//if number of cards in stack less than 9
       {
         Populate();//repopulate the deck
       }
   }
   else
   {
       cout << "Out of cards. Unable to deal.";
   }
}

void Deck::AdditionalCards(GenericPlayer& aGenericPlayer)
{
   cout << endl;
   //continue to deal a card as long as generic player isn't busted and
   //wants another hit
   while ( !(aGenericPlayer.IsBusted()) && aGenericPlayer.IsHitting() )
   {
       Deal(aGenericPlayer);
       cout << aGenericPlayer << endl;
       
       if (aGenericPlayer.IsBusted())
         aGenericPlayer.Bust();
   }
}  

Answer
I am not best pleased to receive such question. The code you post is incomplete (classes Hand and GenericPlayer and all includes are missing), does not compile for various other reasons and is quite difficult to read.

If you are going to post code for review then please distil it down to the minimum required to demonstrate the query - this will focus the expert's attention on the problem and make sure it builds and is neatly formatted and maybe add comments to explain anything odd or that you really want us to focus on. In short make it as easy for the expert to see what you want help with. You might even find that you answer the query (at least in part) yourself while doing this.

Hand::Copy operation:
---------------------
As far as I can tell from the code you post it is not correct. You seem to create the copy of Hands with new default constructed cards instead of cards that are copies of the other Hand.

Desk copy construction and assignment:
-------------------------------------
As far as I can tell the only requirement for Deck copy and assignment is that the base Hand copy or assignment is executed. This will be performed by default anyway so I suggest you remove your explicit Deck::Deck(Deck const&) and Deck::operator=(Deck const&) operations.

Notes:
-----
You can check that the code does what you think by actually building it and creating a test harness for each class that tests each operation. Each test would have an expected result which you check for after performing the operation under test.

For example you can create a test for Hand copy construction:

   Hand h1;

   // set up cards in h1 to non-default values

   Hand h2( h1 ); // copy h1 to h2

   // check that each card in h2 matches the same card in h1

Once you have written the initial set of tests you add to them as you add to the classes. You can then re-run them anytime changes have been made to ensure that nothing is broken. If any tests fail then either the class implementation is broken or the test is broken, and you can fix as appropriate. Such tests are often called call unit tests, and running them after changes is called regression testing.

As you are creating new Cards Hand::Copy can fail due to resource exhaustion (no memory). In this case an exception would be thrown, and I think that your Hand (and Deck) objects would be left in a bad state in this situation in the case of the assignment operation. An exception thrown during copy construction will not create the object and will therefore _not_ cause the Hand destructor to be executed - thus you would 'loose' memory for all the Cards created up to the point the exception is thrown. You might like to read up on exceptions and exception safety see for example http://www.parashift.com/c++-faq-lite/exceptions.html and http://www.gotw.ca/gotw/index.htm especially articles 59..62, 65 and 66.

You are using pointers in your Card vector. Why? Why not make the Card type compatible with vector element types (if it is not already) i.e. it is copyable, assignable and destroyable by a destructor. Some operations will additionally require that Card is default constructible and comparable (operator==). None of these is too difficult to arrange.

If you do this then you can use a vector<Card> instead of vector<Card*>. You should not then require any explicit assignment or copy constructor logic for Hand as the m_Cards member will then have value semantics - meaning it will copy properly by default. Also I suspect that a lot if not all of the exception problems will go away (I would have to think a bit more on it to be sure!).

Hand::Add would then take a const reference to a Card object and you would pass it a (possibly temporary) Card thus:

void Hand::Add(Card const & card)
{
   m_Cards.push_back(card);
}

void Deck::Populate()
{
   Clear();
   //create standard deck
   for (int s = Card::CLUBS; s <= Card::SPADES; ++s)
       for (int r = Card::ACE; r <= Card::KING; ++r)
         Add( Card( static_cast<Card::rank>(r)
         , static_cast<Card::suit>(s)
         )
         );

}

If you do not like all this simplicity then at least use a smart pointer type rather than a raw pointer such as the Boost scoped_pointer (see http://www.boost.org/ and http://www.boost.org/libs/smart_ptr/smart_ptr.htm). Note that the unusual behaviour of std::auto_ptr means that it cannot be used with the standard containers such as std::vector.

Hope this is of use.

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.