0

Ok, so I know there are probably a lot of errors in this code. I'm pretty new to dynamic memory allocation, pointers, etc.

The header file, account.h, is given to us by our professor. We were told not to make any changes to the .h file.

The implementation file is written by me. The main function is included just for basic initial testing. We were given another file to actually test the implementation of the account class.

If I don't comment out the cout name line, I get a seg fault 11 error. If I do, it'll print the account number, but throw this error:

Test(29976) malloc: * error for object 0x62c1aa18c9d8374: pointer being freed was not allocated* set a breakpoint in malloc_error_break to debug
Abort trap: 6

Any help at all would be greatly appreciated!

Here's the header file:

class account
{
public:
    typedef char* string;
    static const size_t MAX_NAME_SIZE = 15;
    // CONSTRUCTOR
    account (char* i_name, size_t i_acnum, size_t i_hsize);
    account (const account& ac);
    // DESTRUCTOR
    ~account ( );
    // MODIFICATION MEMBER FUNCTIONS
    void set_name(char* new_name);
    void set_account_number(size_t new_acnum);
    void set_balance(double new_balance);
    void add_history(char* new_history);
    // CONSTANT MEMBER FUNCTIONS
    char* get_name ( ) const;
    size_t get_account_number ( ) const;
    double get_balance( ) const;
    size_t get_max_history_size( ) const;
    size_t get_current_history_size ( ) const;
    string* get_history( ) const;
    friend ostream& operator <<(ostream& outs, const account& target);
private:
    char name[MAX_NAME_SIZE+1]; //name of the account holder
    size_t ac_number; //account number
    double balance; //current account balance
    string *history; //Array to store history of transactions
    size_t history_size; //Maximum size of transaction history
    size_t history_count; //Current size of transaction history
};

Here is the implementation file:

// File: account.cxx
// Author: Mike Travis
// Last Modified: Mar 3, 2012
// Description: implementation of Account class as prescribed by the file account.h

#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include "account.h"

using namespace std;

//Constructor

account::account(char* i_name, size_t i_acnum, size_t i_hsize){
    string *d_history;
    d_history = new string[i_hsize];

    for(int i = 0; i<i_hsize; i++){
        name[i] = i_name[i];
    }
    ac_number = i_acnum;
    history_size = i_hsize;
    history_count = 0; 
}


account::account(const account& ac){
    string *d_history;
    d_history = new string[ac.history_size];

    for( int i=0; i<ac.get_current_history_size(); i++){
        strcpy(d_history[i], history[i]);
    }

    strcpy(name,ac.get_name());
    ac_number = ac.get_account_number();
    history_size = ac.get_max_history_size();
    history_count = ac.get_current_history_size();
}

account::~account(){    delete [] history;  }

void account::set_name(char* new_name){                 strcpy(name, new_name); }
void account::set_account_number(size_t new_acnum){     ac_number = new_acnum;  }
void account::set_balance(double new_balance){          balance = new_balance;  }
void account::add_history(char* new_history){
    strcpy(history[history_count], new_history);
    history_count++;
}

char* account::get_name() const {
    char* name_cpy;
    strcpy(name_cpy, name);
    return name_cpy;
}

size_t account::get_account_number() const{             return ac_number;       }
double account::get_balance() const{                    return balance;         }
size_t account::get_max_history_size() const{           return history_size;    }
size_t account::get_current_history_size() const{       return history_count;   }
//string* account::get_history() const{                         return *history;        }


int main(){

    account test1("mike travis", 12345, 20);
    //cout<<"\nname: "<< test1.get_name();

    cout<<"\n\nacnum: "<<test1.get_account_number()<<"\n\n";

    return 0;
}
Peter O.
  • 32,158
  • 14
  • 82
  • 96
MRT89
  • 299
  • 3
  • 7
  • 16
  • 1
    If you are writing C++, you should prefer `new`/`delete` to `malloc`/`free`. And you should use smart pointers to manage things, unless you really know what you're doing. – Oliver Charlesworth Mar 03 '12 at 22:01
  • The error tells you how to debug this: "set a breakpoint in malloc_error_break to debug." Have you done this? What did you discover? – James McNellis Mar 03 '12 at 22:02
  • 4
    If your instructor gave you that header file, please inform him that he should pick up [a good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and not teach students to write poor quality C++ code. It is impossible to implement that class correctly: it owns resources but does not provide a user-declared copy assignment operator. – James McNellis Mar 03 '12 at 22:04
  • 2
    The header files are so bad, it's not even funny. Furthermore, you don't seem to understand any of this, I don't know were to begin. Take a look at your get_name implementation. What do you think this is doing? First you write to an uninitialized pointer, and then you're returning it's value. But why are you copying there at all? Oh I see, your prof doesn't seem to know const correctness. Conclusion: Please learn the parts separately and ask smaller question. -- With this header file, it _is not possible_ to write clean code. Just look at the get_name() declaration. – cooky451 Mar 03 '12 at 22:11
  • no i havent set a breakpoint in malloc_error_break... im using g++ from mac terminal to compile, not sure how to set a symbolic breakpoint in malloc_error_break... anyone know how i would do this? – MRT89 Mar 03 '12 at 22:13
  • and @OliCharlesworth I'm using new/delete, not malloc/free. Thats another reason this error is confusing/frustrating. Note, in the constructors i use the new operator. – MRT89 Mar 03 '12 at 22:15
  • @JamesMcNellis this is not the only problem of this class. Non-virtual destructor, non-consistent usage of typedefed `string`, non-const pointers returned by const methods (so he made desparate strcpy)... – mip Mar 03 '12 at 22:19
  • @cooky451 here is the description for the get_name() function in the header file: char* get_name ( ) const; Postcondition: Pointer to a copy of name has been returned. Memory for the copy has been allocated using new operator. – MRT89 Mar 03 '12 at 22:19
  • @Mtravis The problem is that no one expects a get() function to return a pointer which the caller has to delete. It's ridiculous. That function of course needs to return at least a const char*. Seriously, how do you gonna work with that header? It's just impossible. – cooky451 Mar 03 '12 at 22:24
  • @doc: No, I did not enumerate all of the flaws in the class definition. I mentioned only the flaw that is itself sufficient to make it impossible to implement the class correctly. That said, I disagree on one point: a virtual destructor is a feature. If the constructor does not need to be virtual, it should not be declared virtual. There is nothing in the question that states or implies that `account` will be derived from. – James McNellis Mar 03 '12 at 22:28
  • @JamesMcNellis I understand your point of view. But if I can share my reflections with you - I came to not a bad practice I think, so that in such cases I am using keyword `struct` instead of `class`. It tells me that instance will not behave like class and I have usage for this two C++ synonyms. Structures are not virtual in my world :) – mip Mar 03 '12 at 22:45

2 Answers2

1

In the destructor of account, you delete the history array. However, in the constructor, you allocate (and leak) an array which is stored in the local variable d_history. You presumably wanted to assign that to the member variable history instead - since you haven't, if you get to the destructor it gives you an error saying that you're freeing history but have never allocated it.

There's a similar error in the copy constructor as well.

There are also other errors in your code as well, which I assume you'll find as you go - get_name(), for example, is not going to work. I suspect the header file is not helping here, but there's not much to be done if you're not supposed to change that.

Peter
  • 7,216
  • 2
  • 34
  • 46
0

I've written a little bit code for you and corrected the epic mistakes (even in the header file, sorry ;)). It is still extremely ugly and c-ish, but maybe you can learn something reading it:

#include <cstddef>
#include <ostream>

class account
{
    // The whole class makes no sense, since it has no useful
    // member function or anything like this.
    // Furthermore, the class provides almost full access to all its member variables.
    // At this point one could just make everything public.

    // This is not even exception safe when the constructor throws.
    // A good implementation would use history_entry and history classes, 
    // together with std::string and std::vector/std::deque
    // And it would provide some sort of functionality. ;)

public:
  account(const char* name, unsigned number, std::size_t history_max_size);
  account(const account& other);
  ~account();

    const char* name() const;
  unsigned number() const;
  double balance() const;
    const char* const* history() const;
    std::size_t history_size() const;
  unsigned history_max_size() const;

  void set_name(const char* new_name);
  void set_number(unsigned new_number);
  void set_balance(double new_balance);
  void add_history(const char* new_history);

private:
  char* name_;
  unsigned number_;
  double balance_;
  char** history_;
    std::size_t history_size_;
    const std::size_t history_max_size_;
};

std::ostream& operator << (std::ostream& stream, const account& a);


#include <cassert>
#include <cstring>

account::account(const char* name, unsigned number, std::size_t history_max_size)
    : name_(0)
    , number_(number)
    , balance_(0.0)
    , history_(new char*[history_max_size])
    , history_size_(0)
    , history_max_size_(history_max_size)
{
    assert(name != 0);
    assert(history_max_size != 0);
    set_name(name);
}

account::account(const account& other)
    : name_(0)
    , number_(other.number_)
    , balance_(other.balance_)
    , history_(new char*[other.history_max_size_])
    , history_size_(other.history_size_)
    , history_max_size_(other.history_max_size_)
{
    set_name(other.name_);
    for (std::size_t i = 0; i != other.history_size_; ++i)
    {
        history_[i] = new char[std::strlen(other.history_[i]) + 1];
        strcpy(history_[i], other.history_[i]);
    }
}

account::~account()
{
    delete[] name_;

    for (std::size_t i = 0; i != history_size_; ++i)
        delete[] history_[i];
    delete[] history_;
}

const char* account::name() const
{
    return name_;
}

unsigned account::number() const
{
    return number_;
}

double account::balance() const
{
    return balance_;
}

const char* const* account::history() const
{
    return history_;
}

std::size_t account::history_size() const
{
    return history_size_;
}

unsigned account::history_max_size() const
{
    return history_max_size_;
}

void account::set_name(const char* new_name)
{
    if (name_)
        delete[] name_;

    name_ = new char[std::strlen(new_name) + 1];
    std::strcpy(name_, new_name);
}

void account::set_number(unsigned new_number)
{
    number_ = new_number;
}

void account::set_balance(double new_balance)
{
    balance_ = new_balance;
}

void account::add_history(const char* new_history)
{
    if (history_size_ == history_max_size_)
    {
        delete[] history_[0]; // delete oldest entry
        for (std::size_t i = 0; i != history_size_ - 1; ++i)
            history_[i] = history_[i + 1];
        --history_size_;
    }

    history_[history_size_] = new char[strlen(new_history) + 1];
    std::strcpy(history_[history_size_], new_history);
    ++history_size_;
}

std::ostream& operator << (std::ostream& stream, const account& a)
{
    return stream << "account [name: " << a.name() << ", number: " 
        << a.number() << ", balance: " << a.balance() << ']';
}


#include <iostream>

int main()
{
    account a("Hello!", 500, 5);
    a.set_balance(12.546);
    for (int i = 50; i--; )
        a.add_history("Yaaay..");
    //account b = a;
    std::cout << a << '\n';
}
cooky451
  • 3,460
  • 1
  • 21
  • 39