0

I have one parent and two children, I want to create a dynamic array that contains the children (not allowed to use vectors). I created the classes and everything, and I get no compilation errors or warnings, but when I try to access a member of the array and print its content, nothing happens.

I'm aware there may be some bad coding style here, but I'm only interested in knowing why the array isn't working. I think it's because I created an array of objects, instead of an array of pointers to objects, I'm not sure.

(I removed the 2nd child from the code because it's irrelevant here)

#pragma warning(disable:4996)
using namespace std;
#include <iostream>
#include <cstdlib>
#include <cctype>
#include "date.h"

#define ADDRESS_SIZE 512
#define NAME_SIZE 128
#define DATE_SIZE 128



class Treatment {
protected:
    int treatment_ID;
    int patient_ID;
    Date treatment_date;
    int treatment_costs;
public:
    Treatment() {}
    Treatment(int treatment_ID, int patient_ID, int treatment_costs, char treatment_date[DATE_SIZE]) {
        this->treatment_ID = treatment_ID;
        this->patient_ID = patient_ID;
        this->treatment_costs = treatment_costs;
        this->treatment_date.convert_date(treatment_date);
    }

    void set_treatment_ID(int treatment_ID) {
        this->treatment_ID = treatment_ID;
    }

    void read_treatment_date(void) {
        treatment_date.read_date();
    }

    void set_treatment_date(char _date[DATE_SIZE]) {
        treatment_date.convert_date(_date);
    }

    void set_patient_ID(int patient_ID) {
        this->patient_ID = patient_ID;
    }
    void set_treatment_costs(int treatment_costs) {
        this->treatment_costs = treatment_costs;
    }

    int get_treatment_ID(void) {
        return treatment_ID;
    }
    int get_patient_ID(void) {
        return patient_ID;
    }
    int get_treatment_costs(void) {
        return treatment_costs;
    }

};

class Outside_treatment : public Treatment {
private:
    int clinic_num;
    //class doctor;
public:
    Outside_treatment() {}
    Outside_treatment(int clinic_num, int treatment_ID, int patient_ID, int treatment_costs, char treatment_date[DATE_SIZE]) :Treatment(treatment_ID, patient_ID, treatment_costs, treatment_date) {
        this->clinic_num = clinic_num;
    }
    void set_clinic_num(int clinic_num) {
        this->clinic_num = clinic_num;
    }
    int get_clinic_num() {
        return this->clinic_num;
    }

    void print_outside_treatment(void) {
        cout << "The clinic num is " << clinic_num << "\n";
        cout << "The treatment ID is " << treatment_ID << "\n";
        cout << "The patient_ID is " << patient_ID << "\n";
        cout << "The treatment costs are " << treatment_costs << "\n";
        treatment_date.print_date();
        cout << " treatment date in compare format is " << treatment_date.get_compare();

    }

};

class Dynarray {
private:
    Treatment *pa;
    int length;
    int nextIndex;
public:
    Dynarray() {
        pa = new Treatment[10];
        length = 10;
        nextIndex = 0;
    }
    ~Dynarray() {
        delete[]pa;
    }
    void add(Treatment &add) {
        Treatment *pnewa;
        if (nextIndex == length) {
            length += 10;
            pnewa = new Treatment[length];
            for (int i = 0; i < nextIndex; ++i) {
                pnewa[i] = pa[i];
            }
            delete[]pa;
            pa = pnewa;
        }
        pa[nextIndex++] = add;
    }
    Treatment &operator[](int index) {
        return *(pa + index);
    }
};

int main(void) {
    Outside_treatment it;

    cout << "Enter the patient ID\n";
    int p_id;
    cin >> p_id;
    it.set_patient_ID(p_id);
    cin.ignore();
    cout << "set treatment date\n";
    it.read_treatment_date();

    cout << "enter costs\n";
    int costs;
    cin >> costs;
    it.set_treatment_costs(costs);

    cout << "enter section num\n";
    int sc_num;
    cin >> sc_num;
    it.set_clinic_num(sc_num);

    it.print_outside_treatment();

    Dynarray da;

    da.add(it);

    Treatment *yetanotherpointer = &da[0];


    int i = yetanotherpointer->get_patient_ID();
    cout << i << endl;

    while (1);
};
11
  • Hint: Use std:vector instead of new[]. It'll save you a ton of pain and trouble. Also if your program isn't behaving like you expect it to, step through with a debugger to see what's happening. Commented Dec 13, 2017 at 23:40
  • As I've said in the post, my college doesn't allow me to use vectors. Commented Dec 13, 2017 at 23:43
  • 4
    Why do people insist on teaching "C++" courses that utterly fail to teach any meaningful C++? It's ridiculous. It's like they start with using namespace std;, which is a bad habit to adopt, and things go sharply downhill from there. I'm all for teaching things from first principles, but even the book Bjarne wrote, which does that, shows how the Standard Library containers are built from primitives, it doesn't reinvent the wheel, just shows you how their wheel was made. Hope you can do what you've got to do to pass, then. Debugger time! Commented Dec 13, 2017 at 23:47
  • aha - the usual 'dont use the correct tool for the job, instead hack something you will never use again ' BS. The only justification for this is if this is a course trying to show you how to create std::vector like class Commented Dec 13, 2017 at 23:48
  • 1
    You have an array of Treatment and the term you need to search for is "slicing". Commented Dec 13, 2017 at 23:54

1 Answer 1

1

You are creating an array of Treatment object instances. As such, you cannot store any derived types at all. If you try to assign an object of a derived type to a Treatmentobject, it will get sliced.

Your hunch is correct. Since you are dealing with polymorphic types, you need to store pointers to objects, not actual objects, in the array. Then you can store Treatment* pointers to objects of any derived type, eg:

class Treatment {
    ...
public:
    ...

    // make the base class destructor virtual so calling
    // 'delete' on a base class pointer will invoke derived
    // destructors. Otherwise, if a derived class has any
    // data members with a non-trivial destructor, you will
    // cause leaking.  When writing polymorphic classes, it
    // is common practice to always define a virtual destructor,
    // just in case...
    //
    virtual ~Treatment() {} // <-- add this
    ..
};

class Dynarray {
private:
    Treatment **pa;
    int length;
    int capacity;

    // prevent copying the array, otherwise you risk
    // memory errors if multiple arrays own the same
    // objects being pointed at and try to free them
    // multiple times. Copying pointers to owned objects
    // is not safe, you need a cloning mechanism to
    // make deep-copies of derived types so a copied
    // array can point to its own objects...
    Dynarray(const Dynarray &) {}
    Dynarray& operator=(const Dynarray &) {}

public:
    Dynarray(int initialCapacity = 10) {
        pa = new Treatment*[initialCapacity];
        capacity = initialCapacity;
        length = 0;
    }

    ~Dynarray() {
        for (int i = 0; i < length; ++i)
            delete pa[i];
        delete[] pa;
    }

    void add(Treatment *add) {
        if (length == capacity) {
            Dynarray temp(capacity + 10);
            for (int i = 0; i < length; ++i) {
                temp.pa[i] = pa[i];
            }
            Treatment *ptr = temp.pa;
            temp.pa = pa;
            pa = ptr;
            capacity = temp.capacity;
        }

        pa[length++] = add;
    }

    Treatment* operator[](int index) {
        return pa[index];
    }
};

int main(void) {
    Outside_treatment *ot = new Outside_treatment;

    cout << "Enter the patient ID\n";
    int p_id;
    cin >> p_id;
    ot->set_patient_ID(p_id);
    cin.ignore();
    cout << "set treatment date\n";
    ot->read_treatment_date();

    cout << "enter costs\n";
    int costs;
    cin >> costs;
    ot->set_treatment_costs(costs);

    cout << "enter section num\n";
    int sc_num;
    cin >> sc_num;
    ot->set_clinic_num(sc_num);

    ot->print_outside_treatment();

    Dynarray da;
    da.add(ot);

    Treatment *t = da[0];

    int i = t->get_patient_ID();
    cout << i << endl;

    while (1);
};

On the other hand, if the array doesn't need to own the objects being pointed at, just store the pointers, you can remove the delete loop in the destructor, and the caller is not required to use new to create objects being stored, and making a copy of the array is safe, eg:

class Dynarray {
private:
    Treatment **pa;
    int length;
    int capacity;

public:
    Dynarray(int initialCapacity = 10) {
        pa = new Treatment*[initialCapacity];
        capacity = initialCapacity;
        length = 0;
    }

    // coping is OK since the objects being pointed
    // at are not owned by the array!
    Dynarray(const Dynarray &src) {
        pa = new Treatment*[src.capacity];
        capacity = src.capacity;
        length = src.length;
        for (int i = 0; i < length; ++i) {
            pa[i] = src.pa[i];
        }
    }

    Dynarray& operator=(const Dynarray &rhs) {
        if (&rhs != this) {
            Dynarray temp(rhs);
            Treatment **ptr = temp.pa;
            temp.pa = pa;
            pa = ptr;
            capacity = rhs.capacity;
            length = rhs.length;
        }
        return *this;
    }

    ~Dynarray() {
        delete[] pa;
    }

    void add(Treatment *add) {
        if (length == capacity) {
            Dynarray temp(capacity + 10);
            for (int i = 0; i < length; ++i) {
                temp.pa[i] = pa[i];
            }
            Treatment **ptr = temp.pa;
            temp.pa = pa;
            pa = ptr;
            capacity = temp.capacity;
        }

        pa[length++] = add;
    }

    Treatment* operator[](int index) {
        return pa[index];
    }
};

int main(void) {
    Outside_treatment ot;

    cout << "Enter the patient ID\n";
    int p_id;
    cin >> p_id;
    ot.set_patient_ID(p_id);
    cin.ignore();
    cout << "set treatment date\n";
    ot.read_treatment_date();

    cout << "enter costs\n";
    int costs;
    cin >> costs;
    ot.set_treatment_costs(costs);

    cout << "enter section num\n";
    int sc_num;
    cin >> sc_num;
    ot.set_clinic_num(sc_num);

    ot.print_outside_treatment();

    Dynarray da;
    da.add(&ot);

    Treatment *t = da[0];

    int i = t->get_patient_ID();
    cout << i << endl;

    while (1);
};
Sign up to request clarification or add additional context in comments.

6 Comments

virtual ~Treatment() {} // <-- add this - Are you sure that this is required?
@EdHeal: If the base class destructor is not virtual, calling delete on a base class pointer will not invoke derived destructors. This is OK as long as the derived classes do not have any data members with non-trivial destructors. If they do, the virtual destructor is required to avoid leaks. When writing polymorphic classes, it is common practice to always define a virtual destructor, just in case.
I'm familiar with slicing, but I only need to access parent methods, so I don't think it plays a part here. I copy pasted your code but it didn't seem to print either, anyway, you've fixed it enough that I can debug on my own now. Thanks.
@MichaelX: slicing isn't about methods, it is about data. If you slice an Outside_treatment object, you lose its clinic_num data. Then there is no point in creating an Outside_treatment object in the first place.
virtual get_clinic_num() would fix that, correct?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.