Search code examples
c++stringpointersinheritancedynamic-memory-allocation

Initializing parent class' pointer using initializer list


I have a class strings:

    class strings
    {
    protected:
    string *ptr;
    int size;
public:
    strings() {
        ptr = NULL;
        size = -1;
    }
    strings(int size) {
        this->size = size;
        ptr = new string[size];
    }
    string* retPtr() {
        return ptr;
    }
    void setPtr(int size)
    {
        ptr = new string[size];
        this->size = size;
    }
    strings(const strings& obj) {
        this->size = obj.size;
        for (int i = 0;i < size;++i)
            this->ptr[i] = obj.ptr[i];
    }
    friend istream& operator>>(istream& input, strings& obj) {
        cin.ignore();
        cout << "Enter " << obj.size << " string one by one:\n";
        for (int i = 0;i < obj.size;++i)
        {
            getline(input, obj.ptr[i]);
        }
        return input;
    }
    friend ostream& operator<<(ostream& output, const strings& obj) {
        cout << "Strings are:\n";
        for (int i = 0;i < obj.size;++i)
            output << obj.ptr[i] << "\n";
        return output;
    }
    void operator =(const strings& obj)
    {
        this->size = obj.size;
        for (int i = 0;i < size;++i)
            ptr[i] = obj.ptr[i];
    }
    ~strings()
    {
        delete[]ptr;
    }
};

Another class stringsFromNumbers:

class stringsFromNumbers:public strings
{
    int numbers;
public:
    stringsFromNumbers(){
        numbers = -1;
    }
    stringsFromNumbers(int size, int numbers):strings(size){
        this->numbers = numbers;
    }
    stringsFromNumbers(const stringsFromNumbers& obj)
    {
        this->numbers = obj.numbers;
        this->size = obj.size;
        for (int i = 0;i < size;++i)
            this->ptr[i] = obj.ptr[i];
    }
    friend istream& operator>>(istream& input, stringsFromNumbers& obj) {
        cin.ignore();
        cout << "Enter " << obj.size << " string one by one:\n";
        for (int i = 0;i < obj.size;++i)
        {
            getline(cin, obj.ptr[i]);
        }
        return input;
    }
    friend ostream& operator<<(ostream& output, const stringsFromNumbers& obj) {
        cout << "Numbers are: " << obj.numbers;
        cout << "\nStrings are:\n";
        for (int i = 0;i < obj.size;++i)
            output << obj.ptr[i] << "\n";
        return output;
    }
    void operator =(const stringsFromNumbers& obj)
    {
        this->numbers = obj.numbers;
        this->size = obj.size;
        for (int i = 0;i < size;++i)
            this->ptr[i] = obj.ptr[i];
    }
    ~stringsFromNumbers()
    {
        delete[] ptr;
    }
};

Whenever i try execute this line of code:

stringsFromNumbers obj2(N, P);

where N and P are valid integers, I get an "Access Reading Violation", do you see something wrong in the code? I have been stuck on this for almost 2 hours. I have tried debugging and fixing it, i have also tried multiple other methods. The exception takes me to this function:

inline void _Container_base12::_Orphan_all() noexcept {
#if _ITERATOR_DEBUG_LEVEL == 2
    if (_Myproxy) { // proxy allocated, drain it
        _Lockit _Lock(_LOCK_DEBUG);

        for (auto _Pnext = &_Myproxy->_Myfirstiter; *_Pnext; *_Pnext = (*_Pnext)->_Mynextiter) {
            (*_Pnext)->_Myproxy = nullptr;
        }

        _Myproxy->_Myfirstiter = nullptr;
    }
#endif // _ITERATOR_DEBUG_LEVEL == 2
}

The problem is with this function most probably but how can it be fixed?

stringsFromNumbers(int size, int numbers):strings(size){
    this->numbers = numbers;
}

Solution

  • Both stringsFromNumbers::~stringsFromNumbers and strings::strings call delete[] on ptr, so the array is attempted to be freed twice; the second time causes an issue.

    Only one of these classes should be responsible for managing the lifecycle of ptr. Remove the delete[] from stringsFromNumbers::~stringsFromNumbers.

    If in some other case you've got a valid reason to free the array in the destructor of a subclass, make sure the parent class destructor is able to deal with the new state (e.g. by setting ptr to nullptr after deleting it), but in general you should avoid the complexity of making both classes sharing responsibility for freeing the memory.