user3712524
user3712524

Reputation: 101

Casting objects in vector in c++

I know this question has already been asked, but none of the answers that I have as of yet found seem to suffice. I am creating a vector of pointers to a base class and adding all sorts of derived classes to it. Now, the base class has a virtual function that is overridden in all of the derived classes and that is unique to each of them. So, when I go through the vector and retrieve those objects and call the function on that object, I need it to call the right one, but all it will do is call the base class version. I am even trying to cast the individual elements back to their original class when I retrieve them from the vector but they refuse to be cast! e.g.

vector<base*> myBase;

DerivedClass *myDerived = static_cast<DerivedClass> myBase[i];

This doesn't work, despite the fact that everything I've read suggests that it should. My debugger says that despite all of this, myDerived is still of type base and it's version of my virtual function is being called.

Any ideas?

class BankAccount {
public:
BankAccount(string namein, string typein){
    name = namein;
    type = typein;
    balance = 0;
}
virtual string getType();
virtual void printTransactions() = 0;
virtual int withdraw(double amt){
    return getBalance() -amt;
}
};

class SavingsAccount: public BankAccount {
public:
SavingsAccount(string namein, string typein);
void addTransaction(string transType, string name);
virtual int withdraw(double amt);
void printTransactions();
virtual string getType();

private:

};

SavingsAccount::SavingsAccount(string namein, string typein): BankAccount(namein, typein) {

}

int SavingsAccount::withdraw(double amt){
double aBal = getBalance() - amt;
if (aBal > 0){
setBalance(aBal);
}
return getBalance() - amt;

}


class CheckingAccount: public SavingsAccount {
public:
CheckingAccount(string nameIn, string typein): SavingsAccount(nameIn, typein){

}
virtual int withdraw(double amt);
void printTransactions();
string getType(){
    return "Checking";
}

};

int CheckingAccount::withdraw(double amtIn){
double newBal = getBalance() - amtIn;
if (newBal < 500.00 && newBal > 2.49) {
    setBalance(newBal - 2.50);
}
return newBal;

}

int main(int argc, const char * argv[])
{
vector<BankAccount*> myAccts;
SavingsAccount *mySav;
CD *myCD;
CheckingAccount *myCheck;

switch (option) {
        case 1: {
            string name;
            string type;
            cout << "Enter name: ";
            cin >> name;
            getline(cin, dump);
            cout << "Enter account type: ";
            cin >> type;
            getline(cin, dump);
            if (type.compare("Checking") == 0) {
                CheckingAccount myCheck1 = CheckingAccount(name, type);
                myAccts.push_back(&myCheck1);

            }
         case 3:{
         for (int x = 0; x < myAccts.size(); x++) {
                if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
                    if (type == "Savings") {
                        mySav = static_cast<SavingsAccount*>(myAccts[x]);
                        double y = mySav->withdraw(amt);
                        if (y < 0){
                            cout << "Insufficient funds!";
                            }
                    }
                     if (type == "Checking") {
                        myCheck = myAccts[x]->GetDerived();
                        double y = myCheck->withdraw(amt);
                        if (y < 0){
                            cout << "Insufficient funds!";
                            }
                        if (y < 497.5) {
                            cout << "Withdrawal fee: $ 2.50" << endl;
                        }


                    }

}

Checking Account is a child of Savings Account. Sorry.

Upvotes: 0

Views: 1194

Answers (3)

Tony Delroy
Tony Delroy

Reputation: 106096

You need to use new to create your accounts... you have:

if (...)
{
    CheckingAccount myCheck1 = CheckingAccount(name, type);
    myAccts.push_back(&myCheck1);
}

...myCheck1 gets destroyed when leaving that if scope, leaving myAccts with a pointer to an effectively random location on the stack that has undefined behaviour if accessed. Change to:

if (type == "Checking")
    myAccts.push_back(new CheckingAccount(name, type));

You will then need to have matching deletes for the vector elements. Googling "C++ new delete tutorial" would be a good idea. The next stage is to learn how to use smart pointers, for example - std::shared_pointer - which remove the burden of remembering to delete.

"Case 3" can be corrected/simplified to:

         for (int x = 0; x < myAccts.size(); x++)
            if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
                double y = myAccts[x]->withdraw(amt); 
                if (y < 0)
                     cout << "Insufficient funds!";
                if (type == "Checking" && y < 497.5)
                        cout << "Withdrawal fee: $ 2.50" << endl;
            }

Notice in particular the double y = myAccts[x]->withdraw(amt); - the virtual function makes sure the right version is called without you having to do anything type-specific in the calling code.

Upvotes: 3

Jay Miller
Jay Miller

Reputation: 2234

Your concept is correct and shouldn't require any casting. The whole point of virtual functions is that if you're holding a base class pointer or reference and call a virtual function, the most derived version of this function will be called at runtime.

The error I see is this:

    if (type.compare("Checking") == 0) {
        CheckingAccount myCheck1 = CheckingAccount(name, type);
        myAccts.push_back(&myCheck1);

You are creating a checking account on the stack, then taking it's address and pushing that address into the vector. At the end of the if block myCheck1 will go out of scope and be destroyed. Your vector will have an address to a location in the stack and you will have Undefined Behavior.

Instead do:

    if (type.compare("Checking") == 0) {
        myAccts.push_back(new CheckingAccount(name, type));

And similar for the other types. Get rid of all of those casts. In this version you will have to delete all of the items in the vector at the end. If you use a std::vector<std::unique_ptr<BankAccount>> then the unique_ptr will take care of cleaning up your allocated objects.

Upvotes: 4

Amadeus
Amadeus

Reputation: 426

Have you tried something like this?

class base
{
public:

inline DerivedClass *GetDerived() {return (DerivedClass*)this;}

...
};

DerivedClass *myDerived = myBase[i]->GetDerived();

Upvotes: 0

Related Questions