Reputation: 681
I'm trying to understand inheritance in C++.
I want to dynamically create an array in a derived class and pass the array pointer to the base class, and then the base class fills in the array. Finally I want to print the array from the derived class.
I wrote the following code but when I run this, the program crashes and I get a segmentation fault error.
How should I implement this?!
class Base {
private:
int* array;
public:
Base(int* a) {
array = a;
for(int i = 0 ; i < 10 ; i++) {
array[i] = i;
}
}
};
class Child : public Base {
private:
int* array;
public:
Child() : array(new int[10]), Base(array) {}
void print() {
for(int i = 0 ; i < 10 ; i++) {
cout << array[i] << endl;
}
}
};
Upvotes: 1
Views: 1395
Reputation: 42909
One solution would be to use std::array
instead of raw pointer like the example below:
#include <iostream>
#include <array>
template<typename T, std::size_t N>
class Base
{
private:
std::array<T, N> const &array;
public:
Base(std::array<T, N> const &a) : array(a) { }
void print() const {
std::cout << "Printing array from Base class!" << std::endl;
for(auto i : array) std::cout << i << " ";
std::cout << std::endl;
}
};
template<typename T, std::size_t N>
class Child : public Base<T, N> {
private :
std::array<T, N> array;
public:
Child() : Base<T, N>(array) {
for(auto &i : array) i = 10;
}
void print() {
std::cout << "Printing array from Child class!" << std::endl;
for(auto i : array) std::cout << i << " ";
std::cout << std::endl;
}
};
auto main() -> int {
Child<int, 10> c;
c.print();
Base<int, 10> *b = &c;
b->print();
return 0;
}
Thus:
You wouldn't have to worry about releasing previously allocated memory.
Your Base
class keeps a constant reference to the array object of the Child class. Thus, you save memory.
Upvotes: 0
Reputation: 726639
The problem here is that the order of items in the initializer list does not influence the order in which the initialization is actually performed; only the order in which the members are declared is considered. Moreover, the initialization of the base always happens first.
Therefore, the Child
constructor is actually executed as follows:
Base(array)
is invoked (array
is not set here!)array
is assigned a valid value.Fortunately, all you need to do to fix this problem is to remove the array
from the Child
: it is redundant anyway:
class Child : public Base {
public:
Child() : Base(new int[10]) {
}
void print() {
for(int i = 0 ; i < 10 ; i++) {
// Make a[] protected in the parent
cout << a[i] << endl;
}
}
};
class Base
{
protected:
int *array;
...
}
If you would rather not make a
protected in the parent, you can fix your code as follows (demo):
Child() : Base(array = new int[10]) {}
This is suboptimal, though, because a redundant member is kept in all instances of Child
.
Upvotes: 5
Reputation: 254471
Base subobjects are initialised before class members, even if you write the initialisers in another order. So here:
Child(): array(new int[10]), Base(array)
the Base
object is initialised first, using the uninitialised value of array
.
Solutions include:
Base::array
accessible from Child
, so it doesn't need its own copy;print
into the base class, so that Child
doesn't need to access array
;Child() : Base(array = new int[10])
, if you really need a redundant copy of an inaccessible variable for some reason.Make sure you've enabled compiler warnings; they should have caught this error.
Upvotes: 3
Reputation: 73386
It's because the base object is constructed first, and only then the child is constructed.
This means that your array is unintialised.
Put some tracing message at the begin and end of each of your consutructors, and you'll get a better understanding of how things work.
Upvotes: 0