Home > OS >  Passing in a class variable to the base class constructor C
Passing in a class variable to the base class constructor C

Time:07-16

I have an implementation of a base class which constructs some array of a size that is known at runtime. When I call the constructor of the base class, I would like to pass in a reference to one of the values in this array. The following example code illustrates this (my actual code includes error handling that is not relevant here).

#include <iostream>

class Base {
    public:
        int & someInteger;

        Base(int & thisIsSomeInteger) : someInteger(thisIsSomeInteger) {}

};

class Impl : public Base {
    public:
        int * someArray;

        Impl(int size) : 
            someArray(new int[size]),
            Base(someArray[2])
        {}
};

int main()
{
    Impl test(5);

    test.someArray[2] = 20;

    // Not the same!
    std::cout << &test.someInteger << "\n";
    std::cout << &test.someArray[2] << "\n";
}

Desired behaviour

Both &test.someInteger and &test.someArray[2] point to the same address in memory.

Actual behaviour

&test.someInteger and &test.someArray[2] point to different addresses in memory. I expect that this is the case because the base class constructor is called first in the initialization list, meaning that someArray hasn't been allocated yet when the base constructor is called.

Possible work around

Initalize someInteger to an address and then change someArray[2] to point to this address instead of whatever address it was allocated to. However, this feels very clunky to me and seems like bad practice.

Is there a better way to solve this problem?

CodePudding user response:

this is the case because the base class constructor is called first in the initialization list

Yes, it is. You can use multiple subclasses to control initialization order.

class Base {
    public:
        int & someInteger;

        Base(int & thisIsSomeInteger) : someInteger(thisIsSomeInteger) {}

};

class ArrayBase {

public:

        int * someArray;
        ArrayBase(int size) : 
            someArray(new int[size]) {}
};

class Impl : public ArrayBase, public Base {
    public:

        Impl(int size) : ArrayBase(size),
            Base(someArray[2])
        {}
};

CodePudding user response:

You could use constructor chaining introducing a private Impl::Impl(int[]) that gets the array as parameter allowing you to determine the reference to pass on to the base class constructor based on the first parameter:

class Impl : public Base {
public:
    int* someArray;

    Impl(int size)
        : Impl(new int[size])
    {
    }

private:
    Impl(int arr[])
        : Base(arr[2]),
        someArray(arr)
    {
    }
};

Note: If you implement destructor logic to free the memory, you need to be extra careful to not access Base::someInteger in Base::~Base(), since Impl::~Impl() executes the base class destructor at its very end after everything Impl-related has been freed.

Also there are potential issues with freeing the array, if Base::Base(int&) can throw and exception; It would be preferrabe to have an object such as std::unique_ptr<int[]> or std::vector<int> owning the array to ensure the memory is freed, if an error happens.

  • Related