-1

I am currently learning basics of c++, and am coming across an issue with the class array below.

class Arrayclass {
private:
    int arraySize;
    float array[];

public:
    Arrayclass(int arraySize) {
        float * array = new float[arraySize]();
}

~Arrayclass() {
   delete[] array;
}
}

As far as I am aware, I am having issues with the array being initialized in the constructor not actually corresponding to a stored array in the new class, thus when I am initializing a new class in my main, the array created holds 0 values. I apologize my explanation is unclear, just trying to understand how the array initialized in the constructor correlates to the array in my private.

  • 1
    You're declaring a local variable `array` in the constructor, it has nothing to do with the data member `array`. Just put `array = new float[arraySize]();` in the constructor. – songyuanyao Oct 04 '21 at 02:52
  • It doesn't help that your constructor defines a new variable named `array` that has no relationship whatsoever to the class member named `array` - and ceases to exist when the constructor returns. Instead, initialise the `array` member in the constructor initialiser list (not in the constructor body) i.e. `ArrayClass(int arraySize) : array(new float[arraySize] {};`. Also, classes that manage a resource (like a dynamically allocated array, in your case) usually need to manually implement copy constructors, copy assignment, and destructors. Look up "rule of three" for information on that. – Peter Oct 04 '21 at 03:10
  • 1
    `float array[];` is not allowed in Standard C++. Maybe you meant `float * array;`, but you should have used `std::vector array;` – M.M Oct 04 '21 at 03:34

1 Answers1

2

float array[]

either is an extension or just doesn't compile. For a C-style static array, Type name[constexpr_size] is used. For a dynamic array, Type* name is stored as a pointer to the beginning of the storage allocated.

Dynamic Array

OK, we need the second option. Then, we start with your snippet

class Array { // "class Arrayclass" -> "class Array", no need to repeat ourselves
private:
    float* array{};
    int size{}; // "Array::arraySize" -> "Array::size", again
public:
    ~Array() { delete[] array; } // your destructor is fine
};

(see member initializer as an explanation to those {} initializers in float* array{} and int size{}).

What about

Arrayclass(int arraySize) { float * array = new float[arraySize](); }

, you create a local float* which dies at the end of scope (=the constructor). You probably meant to store that newly allocated array in (this->)array:

Array(int newSize) { // BAD CODE, see below
    array = new float[newSize]{};
    size = newSize;
}

but the code above is like writing float* p; p = something (create default, than initialize it) instead of float* p{something} (initialize on creation), let's improve it by using member initializer list:

Array(int newSize): array{new float[newSize]{}}, size{newSize} {}

Now, your array (almost) can construct and destruct properly.

==========

However, there's still an issue left: code such as

{
    Array a1{42};
    Array a2{a1}; // copy construction
}

has undefined behavior and in practice (in this case) should/might crash your application. Why? Because it works as something like (sizes omitted):

{
    float* array1{new float[42]{}};
    float* array2{array1};
    delete[] array1;
    delete[] array2; // that UB (double free); the same dynamic array is deleted twice
}

Well, it is a topic for another "lesson". See the rule of three (five) (zero). In general, see RAII and copy/move-semantics.

passing_through
  • 1,778
  • 12
  • 24