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);
};
MichaelX
  • 202
  • 1
  • 9
  • 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. – tadman Dec 13 '17 at 23:40
  • As I've said in the post, my college doesn't allow me to use vectors. – MichaelX Dec 13 '17 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! – tadman Dec 13 '17 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 – pm100 Dec 13 '17 at 23:48
  • i mean this is a good advanced c++ task, create your own equivalent of std::vector. But i doubt thats the point here. The first thing you would do is use std::vector so you get the semantics – pm100 Dec 13 '17 at 23:50
  • any way lets try to fix your issue. "nothing happens" what does that mean? More importantly do you know how to use your debugger? – pm100 Dec 13 '17 at 23:53
  • 1
    You have an array of `Treatment` and the term you need to search for is "slicing". – molbdnilo Dec 13 '17 at 23:54
  • @pm100 The last statement I have, `cout << i < – MichaelX Dec 13 '17 at 23:55
  • `while (1);` is not a good thing to do. You should do `cin>>some_int` if you want to pause – pm100 Dec 13 '17 at 23:56
  • gdb works the same for c++ – pm100 Dec 13 '17 at 23:56
  • You do not need `..(void)` in methods. Please read up about `const`. Please dump` using namespace std;`. Please use `std::vector` and get the lecture to refer to us - real programmers – Ed Heal Dec 14 '17 at 00:43

1 Answers1

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);
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • `virtual ~Treatment() {} // <-- add this` - Are you sure that this is required? – Ed Heal Dec 14 '17 at 00:44
  • @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. – Remy Lebeau Dec 14 '17 at 01:00
  • 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 Dec 14 '17 at 01:03
  • @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. – Remy Lebeau Dec 14 '17 at 01:04
  • `virtual get_clinic_num()` would fix that, correct? – MichaelX Dec 14 '17 at 01:15
  • @MichaelX: That would NOT solve the slicing problem, no. The data is *gone*, it is not even in memory. And besides, it doesn't make sense to add derived-specific functions to a base class anyway – Remy Lebeau Dec 14 '17 at 02:27