3

I am having trouble understanding/fixing a problem I am encountering in my first attempt to create a derived container from an abstract superclass.

It seems that I cannot access the derived class's data from a member function. Each time I attempt it, I get an incorrect value.

tuple.h

class container {
public :
    virtual int get_element ( std::size_t index ) = 0 ;
} ;

class tuple : public container {
private :

    int* arr_ptr { } ;

    std::size_t arr_size{ } ;

public :

    tuple ( ) : arr_ptr { nullptr }, arr_size{ 0 }
    { }

    tuple ( std::initializer_list<int> setter_list ) :
        arr_ptr{ new int [ setter_list.size() ] },
        arr_size{ setter_list.size() }
    { }

    int get_element ( std::size_t index ) override { return arr_ptr[index] ; }
} ;

The call to tuple's member function get_element() in main() is displayed below:

main.cpp

int main ( ) {

  tuple t { 1, 2, 3 } ;

  std::cout << t.get_element( 0 ) ;
      
  return 0 ;
}

The incorrect output on my computer is:

-842150451
11
  • 1
    Note: for many reasons don't show text as an image. It's one of the fastest ways you can kill your question. Always provide text as text. Commented Feb 3 at 20:43
  • 2
    Please make sure you present a minimal reproducible example - the code you present doesn't compile because arr_ptr is not in fact a pointer and cannot be initialised with nullptr and the second member has no name. Commented Feb 3 at 20:45
  • 2
    Where is arr_size declared? Please provide a minimal reproducible example. Commented Feb 3 at 20:45
  • 2
    Always compile your example code and make sure it shows exactly the behaviour you want demonstrated. The more people have to change to beat your example into something they can run, the greater the chances for them to add a new mistake (and answer how to fix that new mistake) or fix the mistake you are asking about (and provide no answer). Commented Feb 3 at 20:47
  • 1
    whenever you suspect something is the root cause, remove the thing and see if the issue persists or not. Remove inheritance, still incorrect output: godbolt.org/z/Gxzvj4cqs. You now have a much simpler issue to solve Commented Feb 4 at 9:10

1 Answer 1

8

Your code exhibits undefined behavior because you are not initializing the elements of your allocated array. You are not copying the values from the initializer_list into your array, so whenever you try to read an element you are getting back whatever random value is in that memory.

Try this:

#include <algorithm> // add this

...

tuple ( std::initializer_list<int> setter_list ) : arr_ptr{ new int [ setter_list.size() ] }, arr_size{ setter_list.size() }
{
    // add this
    std::copy ( setter_list.begin(), setter_list.end(), arr_ptr ) ;

    // alternatively:
    // std::copy_n ( setter_list.begin(), setter_list.size(), arr_ptr ) ;
}

On a side note: your tuple class is missing a destructor to free the array. You need to add that to avoid memory leaks:

~tuple ( ) { delete[] arr_ptr ; }

And, per the Rule of 3/5/0, you should also add copy and move constructors, and a copy+move assignment operator:

tuple ( const tuple &src ) : arr_ptr { new int [ src.arr_size ] }, arr_size { src.arr_size }
{
    std::copy_n ( src.arr_ptr, src.arr_size, arr_ptr ) ;
}

tuple ( tuple &&src ) : arr_ptr{ src.arr_ptr }, arr_size{ src.arr_size }
{
    src.arr_ptr = nullptr;
    src.arr_size = 0;
}

tuple& operator=( tuple rhs )
{
    std::swap( rhs.arr_ptr, arr_ptr );
    rhs.arr_ptr = nullptr;
    std::swap( rhs.arr_size, arr_size );
    rhs.arr_size = 0;
    return *this;
}

Online Demo

Sign up to request clarification or add additional context in comments.

2 Comments

I'm burning through A Tour of C++ as fast as I can. I should have doubled back.
Don't try to do it as fast as you can. Do it at the speed that allows you to internalise the knowledge presented.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.